Ich versuche, beim Hinzufügen zur Liste ein neues Objekt vom Typ T über seinen Konstruktor zu erstellen.
Ich erhalte einen Kompilierungsfehler: Die Fehlermeldung lautet:
'T': Beim Erstellen einer Instanz einer Variablen können keine Argumente angegeben werden
Aber meine Klassen haben ein Konstruktorargument! Wie kann ich das zum Laufen bringen?
public static string GetAllItems<T>(...) where T : new()
{
...
List<T> tabListItems = new List<T>();
foreach (ListItem listItem in listCollection)
{
tabListItems.Add(new T(listItem)); // error here.
}
...
}
c#
.net
generics
new-operator
PFUND.
quelle
quelle
Antworten:
Um eine Instanz eines generischen Typs in einer Funktion zu erstellen, müssen Sie sie mit dem Flag "new" einschränken.
Dies funktioniert jedoch nur, wenn Sie den Konstruktor aufrufen möchten, der keine Parameter hat. Nicht der Fall hier. Stattdessen müssen Sie einen anderen Parameter angeben, mit dem Objekte basierend auf Parametern erstellt werden können. Am einfachsten ist eine Funktion.
Sie können es dann so nennen
quelle
T result = thunk == null ? new T() : thunk();
Der Vorteil davon ist für mich, dass die Logik derT
Erstellung an einem Ort konsolidiert wird, anstatt manchmalT
innerhalb und manchmal außerhalb der Methode zu erstellen .in .Net 3.5 und nachdem Sie die Aktivatorklasse verwenden konnten:
quelle
Activator.CreateInstance
diesem Grund einen dedizierten Konstruktor haben, sieht es so aus, als würde Ihr Konstruktor überhaupt nicht verwendet, und jemand könnte versuchen, ihn zu "bereinigen" und zu löschen (um einen Laufzeitfehler bei zu verursachen einige zufällige Zeit in der Zukunft). Möglicherweise möchten Sie eine Dummy-Funktion hinzufügen, in der Sie diesen Konstruktor verwenden, damit beim Kompilieren ein Kompilierungsfehler angezeigt wird.Da sich niemand die Mühe gemacht hat, die Antwort "Reflection" zu veröffentlichen (was ich persönlich für die beste Antwort halte), geht es so weiter:
Bearbeiten: Diese Antwort ist aufgrund von Activator.CreateInstance von .NET 3.5 veraltet, in älteren .NET-Versionen jedoch weiterhin nützlich.
quelle
Objektinitialisierer
Wenn Ihr Konstruktor mit dem Parameter nichts anderes tut als eine Eigenschaft festzulegen, können Sie dies in C # 3 oder besser mit einem Objektinitialisierer tun, anstatt einen Konstruktor aufzurufen (was, wie bereits erwähnt, unmöglich ist):
Auf diese Weise können Sie auch jederzeit eine beliebige Konstruktorlogik in den Standardkonstruktor (leer) einfügen.
Activator.CreateInstance ()
Alternativ können Sie Activator.CreateInstance () wie folgt aufrufen :
Beachten Sie, dass Activator.CreateInstance einen gewissen Leistungsaufwand verursachen kann , den Sie möglicherweise vermeiden möchten, wenn die Ausführungsgeschwindigkeit oberste Priorität hat und eine andere Option für Sie wartbar ist.
quelle
T
, dass die Invarianten geschützt werden (T
vorausgesetzt, dass > 0 Abhängigkeiten oder erforderliche Werte vorhanden sind, können Sie jetzt Instanzen erstellenT
, die sich in einem ungültigen / unbrauchbaren Zustand befindenT
.Sehr alte Frage, aber neue Antwort ;-)
Die ExpressionTree-Version : (Ich denke, die schnellste und sauberste Lösung)
Wie Welly Tambunan sagte: "Wir könnten auch einen Ausdrucksbaum verwenden, um das Objekt zu erstellen."
Dies erzeugt einen 'Konstruktor' (Funktion) für den angegebenen Typ / die angegebenen Parameter. Es gibt einen Delegaten zurück und akzeptiert die Parametertypen als Array von Objekten.
Hier ist es:
Beispiel MyClass:
Verwendungszweck:
Ein weiteres Beispiel: Übergeben der Typen als Array
DebugView of Expression
Dies entspricht dem generierten Code:
Kleiner Nachteil
Alle Wertetypparameter werden eingerahmt, wenn sie wie ein Objektarray übergeben werden.
Einfacher Leistungstest:
Ergebnisse:
Die Verwendung
Expressions
ist +/- 8-mal schneller als das Aufrufen vonConstructorInfo
und +/- 20-mal schneller als die Verwendung vonActivator
quelle
var myConstructor = CreateConstructor(typeof(MyClass<int>), typeof(int));
das funktioniert gut. Ich weiß es nicht.Dies wird in Ihrer Situation nicht funktionieren. Sie können nur die Einschränkung angeben, dass ein leerer Konstruktor vorhanden ist:
Sie können die Eigenschaftsinjektion verwenden, indem Sie diese Schnittstelle definieren:
Dann könnten Sie Ihre Methode folgendermaßen ändern:
Die andere Alternative ist die
Func
von JaredPar beschriebene Methode.quelle
Sie müssen hinzufügen, wo T: new (), um den Compiler wissen zu lassen, dass T garantiert einen Standardkonstruktor bereitstellt.
quelle
Wenn Sie einfach ein Elementfeld oder eine Eigenschaft mit dem Konstruktorparameter initialisieren möchten, können Sie dies in C #> = 3 ganz einfach tun:
Dies ist das gleiche, was Garry Shutler gesagt hat, aber ich möchte eine zusätzliche Notiz machen.
Natürlich können Sie einen Eigenschaftstrick verwenden, um mehr zu tun als nur einen Feldwert festzulegen. Eine Eigenschaft "set ()" kann jede Verarbeitung auslösen, die zum Einrichten der zugehörigen Felder erforderlich ist, sowie jede andere Notwendigkeit für das Objekt selbst, einschließlich einer Überprüfung, ob eine vollständige Initialisierung stattfinden soll, bevor das Objekt verwendet wird, und eine vollständige Konstruktion simulieren ( Ja, es ist eine hässliche Problemumgehung, aber es überwindet die neue () Einschränkung von M $.
Ich kann nicht sicher sein, ob es sich um ein geplantes Loch oder eine versehentliche Nebenwirkung handelt, aber es funktioniert.
Es ist sehr lustig, wie MS-Leute der Sprache neue Funktionen hinzufügen und keine vollständige Analyse der Nebenwirkungen durchführen. Die gesamte generische Sache ist ein guter Beweis dafür ...
quelle
Ich habe festgestellt, dass die Fehlermeldung "Beim Erstellen einer Instanz des Typparameters T kann keine Argumente angegeben werden" angezeigt wird. Daher musste ich Folgendes tun:
quelle
Wenn Sie Zugriff auf die Klasse haben, die Sie verwenden möchten, können Sie diesen Ansatz verwenden, den ich verwendet habe.
Erstellen Sie eine Schnittstelle mit einem alternativen Ersteller:
Erstellen Sie Ihre Klassen mit einem leeren Ersteller und implementieren Sie diese Methode:
Verwenden Sie jetzt Ihre generischen Methoden:
Wenn Sie keinen Zugriff haben, schließen Sie die Zielklasse ein:
quelle
Das ist ein bisschen mies, und wenn ich ein bisschen mucky sage, meine ich vielleicht abstoßend, aber wenn Sie Ihren parametrisierten Typ mit einem leeren Konstruktor ausstatten können, dann:
Ermöglicht es Ihnen effektiv, ein Objekt aus einem parametrisierten Typ mit einem Argument zu erstellen. In diesem Fall gehe ich davon aus, dass der gewünschte Konstruktor ein einziges Argument vom Typ hat
object
. Wir erstellen eine Dummy-Instanz von T unter Verwendung des leeren Konstruktors für die zulässige Einschränkung und verwenden dann die Reflexion, um einen seiner anderen Konstruktoren zu erhalten.quelle
Ich verwende manchmal einen Ansatz, der den Antworten mit Eigenschaftsinjektion ähnelt, aber den Code sauberer hält. Anstatt eine Basisklasse / Schnittstelle mit einer Reihe von Eigenschaften zu haben, enthält sie nur eine (virtuelle) Initialize () - Methode, die als "Konstruktor des armen Mannes" fungiert. Dann können Sie jede Klasse ihre eigene Initialisierung wie einen Konstruktor ausführen lassen, was auch eine bequeme Möglichkeit zum Umgang mit Vererbungsketten bietet.
Wenn ich mich häufig in Situationen befinde, in denen jede Klasse in der Kette ihre eindeutigen Eigenschaften initialisieren soll, rufen Sie dann die Initialize () - Methode des übergeordneten Elements auf, die wiederum die eindeutigen Eigenschaften des übergeordneten Elements usw. initialisiert. Dies ist besonders nützlich, wenn unterschiedliche Klassen vorhanden sind, jedoch eine ähnliche Hierarchie aufweisen, z. B. Geschäftsobjekte, die DTOs zugeordnet sind: s.
Beispiel, das ein gemeinsames Wörterbuch zur Initialisierung verwendet:
quelle
Wenn Sie lediglich eine Konvertierung von ListItem in Ihren Typ T benötigen, können Sie diese Konvertierung in der Klasse T als Konvertierungsoperator implementieren.
quelle
Ich glaube, Sie müssen T mit einer where-Anweisung einschränken, um nur Objekte mit einem neuen Konstruktor zuzulassen.
Jetzt akzeptiert es alles, einschließlich Objekte ohne es.
quelle