Gibt es einen vernünftigen Ansatz für Standardparameter in C # Generics?


91

In C ++ - Vorlagen kann angegeben werden, dass ein bestimmter Typparameter ein Standard ist. Das heißt, wenn nicht ausdrücklich angegeben, wird Typ T verwendet.

Kann dies in C # durchgeführt oder angenähert werden?

Ich suche so etwas wie:

public class MyTemplate<T1, T2=string> {}

Damit eine Instanz des Typs, die nicht explizit angibt T2:

MyTemplate<int> t = new MyTemplate<int>();

Wäre im Wesentlichen:

MyTemplate<int, string> t = new MyTemplate<int, string>();

Letztendlich betrachte ich einen Fall, in dem es eine Vorlage gibt, die ziemlich weit verbreitet ist, aber ich denke darüber nach, sie um einen zusätzlichen Typparameter zu erweitern. Ich könnte eine Unterklasse bilden, aber ich war neugierig, ob es in diesem Sinne andere Optionen gibt.

Antworten:


76

Unterklassen sind die beste Option.

Ich würde Ihre generische Hauptklasse unterordnen:

class BaseGeneric<T,U>

mit einer bestimmten Klasse

class MyGeneric<T> : BaseGeneric<T, string>

Dies macht es einfach, Ihre Logik an einem Ort (der Basisklasse) zu halten, aber auch beide Verwendungsoptionen bereitzustellen. Abhängig von der Klasse ist wahrscheinlich nur sehr wenig zusätzliche Arbeit erforderlich, um dies zu erreichen.


1
ah ... das macht Sinn. Darf der Typname identisch sein, wenn die Typparameter eine eindeutige Signatur bereitstellen?
el2iot2

2
@ee: Ja, Generika sind overloadablenach Parameteranzahl.
Mehrdad Afshari

@ee: Ja, aber ich wäre vorsichtig, wenn ich das mache. In .NET ist dies "legal", dies kann jedoch zu Verwirrung führen. Ich möchte lieber, dass der Name des vom String abgeleiteten Typs der generischen Hauptklasse ähnelt (es ist also offensichtlich, was er ist / leicht zu finden), aber ein Name, der deutlich macht, dass es sich um einen String handelt.
Reed Copsey

@Reed: Führt das wirklich zu Verwirrung? Für diesen speziellen Fall denke ich, dass es sogar hilft, den gleichen Namen zu haben. In .NET gibt es Beispiele, die dasselbe tun: Zum Beispiel Func <> delegieren.
Mehrdad Afshari

4
Zum Beispiel ist Prädikat <T> nur Func <T, bool>, wird jedoch umbenannt, da es einem anderen Zweck dient.
Reed Copsey

19

Eine Lösung ist die Unterklasse. Eine andere, die ich stattdessen verwenden würde, sind Factory-Methoden (kombiniert mit dem Schlüsselwort var).

public class MyTemplate<T1,T2>
{
     public MyTemplate(..args..) { ... } // constructor
}

public static class MyTemplate{

     public static MyTemplate<T1,T2> Create<T1,T2>(..args..)
     {
         return new MyTemplate<T1, T2>(... params ...);
     }

     public static MyTemplate<T1, string> Create<T1>(...args...)
     {
         return new MyTemplate<T1, string>(... params ...);
     }
}

var val1 = MyTemplate.Create<int,decimal>();
var val2 = MyTemplate.Create<int>();

Im obigen Beispiel val2handelt es sich um einen Typ MyTemplate<int,string> und nicht um einen davon abgeleiteten Typ.

Ein Typ class MyStringTemplate<T>:MyTemplate<T,string>ist nicht der gleiche Typ wie MyTemplate<T,string>. Dies kann in bestimmten Szenarien zu Problemen führen. Zum Beispiel können Sie keine Instanz von MyTemplate<T,string>to umwandeln MyStringTemplate<T>.


3
Dies ist der am besten verwendbare Ansatz. Sehr schöne Lösung
T-Moty

12

Sie können auch eine solche Klassenüberladung erstellen

public class MyTemplate<T1, T2> {
    public T1 Prop1 { get; set; }
    public T2 Prop2 { get; set; }
}

public class MyTemplate<T1> : MyTemplate<T1, string>{}

Bitte lesen Sie andere Antworten, bevor Sie eine späte Antwort veröffentlichen, da Ihre Lösung wahrscheinlich dieselbe ist wie die anderer.
Cheng Chen

7
Die akzeptierte Antwort war das Erstellen einer Klasse mit einem anderen Namen. Meine Lösung ist das Überladen derselben Klasse
Nerdroid

2
Nein, beide erstellen eine neue Klasse. Der Name spielt hier keine Rolle. MyTemplate<T1>ist eine andere Klasse als MyTemplate<T1, T2>keine AnotherTemplate<T1>.
Cheng Chen

7
Selbst wenn die realen Typen unterschiedlich sind, was klar und korrekt ist, ist die Codierung einfacher, wenn Sie denselben Namen behalten. Es ist nicht für jeden offensichtlich, dass die Klasse "Überladung" auf diese Weise verwendet werden kann. @DannyChen hat recht - aus technischer Sicht sind die Ergebnisse gleich, aber diese Antwort ist näher, um das zu erreichen, was OP verlangt.
Kuba

1
Leider ist diese Lösung echten "Standard" -Arbeitsargumenten immer noch unterlegen, da a MyTemplate<T1, string>nicht zugewiesen werden kann MyTemplate<T1>, was wünschenswert sein kann
Felk

9

C # unterstützt eine solche Funktion nicht.

Wie Sie sagten, können Sie es in Unterklassen unterteilen (wenn es nicht versiegelt ist und alle Konstruktordeklarationen duplizieren), aber es ist eine ganz andere Sache.


2

Leider unterstützt C # nicht, was Sie versuchen zu tun. Die Implementierung wäre schwierig, da der Standardtyp für einen Parameter die allgemeinen Einschränkungen einhalten müsste und höchstwahrscheinlich Kopfschmerzen verursachen würde, wenn die CLR versuchte, die Typensicherheit zu gewährleisten.


1
Nicht wirklich. Es könnte mit einem Attribut (wie Standardparametern in VB.NET) geschehen sein und vom Compiler beim Kompilieren ersetzt werden. Der Hauptgrund sind die C # -Designziele.
Mehrdad Afshari

Der Compiler muss sicherstellen, dass der Standardparameter die allgemeinen Einschränkungen erfüllt. Außerdem wäre der Standardparameter selbst eine generische Einschränkung, da alle Annahmen über den Typparameter in der Methode erfordern würden, dass alle nicht standardmäßigen Typparameter von ihm erben.
Andrew Hare

@ Andrew, der Standardparameter muss keine generische Einschränkung sein. Wenn sich dies eher wie Standardvorlagenparameter in C ++ verhalten würde, wäre es vollkommen in Ordnung, die Klasse von automatonic zu erweitern: MyTemplate <int, float> x = null Da T2 keine generischen Einschränkungen hat, ist float trotz des Standardtyps der Zeichenfolge in Ordnung . Auf diese Weise sind Standardvorlagenparameter im Wesentlichen nur "syntaktischer Zucker" zum Schreiben von MyTemplate <int> als Abkürzung für MyTemplate <int, string>.
Tyler Laing

@ Andrew, ich stimme zu, dass der C # -Compiler überprüfen sollte, ob solche Standardvorlagenparameter vorhandenen generischen Konstanten entsprechen. Der C # -Compiler überprüft bereits ähnliche Elemente in Klassendeklarationen, z. B. fügt er Reeds BaseGeneric <T, U> -Klasse eine generische Einschränkung wie "where U: ISomeInterface" hinzu, und MyGeneric <T> kann dann nicht mit einem Fehler kompiliert werden. Das Überprüfen eines Standardvorlagenparameters wäre sehr ähnlich: Der Compiler überprüft die Deklaration einer Klasse und die Fehlermeldung kann gleich oder sehr ähnlich sein.
Tyler Laing

"Es wäre schwierig, diese Funktion zu implementieren" Das ist Unsinn. Die Implementierung wäre trivial. Das Überprüfen eines Typs anhand der Vorlageneinschränkungen wird nicht schwieriger, da der Kandidatentyp an einer anderen Stelle in der Datei angezeigt wird (dh in der Vorlage und nicht bei der Vorlageninstanziierung).
Schlamm
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.