Wenn BaseFruit
ein Konstruktor einen akzeptiert int weight
, kann ich ein Stück Obst in einer generischen Methode wie dieser instanziieren?
public void AddFruit<T>()where T: BaseFruit{
BaseFruit fruit = new T(weight); /*new Apple(150);*/
fruit.Enlist(fruitManager);
}
Ein Beispiel wird hinter Kommentaren hinzugefügt. Es scheint, dass ich dies nur tun kann, wenn ich BaseFruit
einen parameterlosen Konstruktor gebe und dann alles über Mitgliedsvariablen ausfülle. In meinem wirklichen Code (nicht über Obst) ist dies ziemlich unpraktisch.
-Update-
Es scheint also, dass es dann in keiner Weise durch Einschränkungen gelöst werden kann. Aus den Antworten ergeben sich drei mögliche Lösungen:
- Fabrikmuster
- Reflexion
- Aktivator
Ich neige dazu zu denken, dass Reflexion die am wenigsten saubere ist, aber ich kann mich nicht zwischen den beiden anderen entscheiden.
Antworten:
Zusätzlich ein einfacheres Beispiel:
Beachten Sie, dass die Verwendung der new () - Einschränkung für T nur dazu dient, dass der Compiler zur Kompilierungszeit nach einem öffentlichen parameterlosen Konstruktor sucht. Der tatsächliche Code, der zum Erstellen des Typs verwendet wird, ist die Activator-Klasse.
Sie müssen sich über den vorhandenen spezifischen Konstruktor informieren, und diese Art von Anforderung kann ein Codegeruch sein (oder eher etwas, das Sie in der aktuellen Version auf c # vermeiden sollten).
quelle
new object[] { weight }
.CreateInstance
wird mit params deklariertpublic static object CreateInstance(Type type, params object[] args)
, also kannst du es einfach tunreturn (T) Activator.CreateInstance(typeof(T), weight);
. Wenn mehrere Parameter vorhanden sind, übergeben Sie diese als separate Argumente. Nur wenn Sie bereits eine Aufzählung von Parametern erstellt haben, sollten Sie sich die Mühe machen, diese zu konvertierenobject[]
und an diese zu übergebenCreateInstance
.Func
die die neue Instanz erstellt. Angenommen,Apple
die Verwendung des Konstruktors istnew Apple(wgt)
. Fügen Sie dannApple
diese Definition zur Klasse hinzu:static Func<float, Fruit> CreateOne { get; } = (wgt) => new Apple(wgt);
In Factory Definepublic static Fruit CreateFruitGiven(float weight, Func<float, Fruit> createOne) { return createOne(weight); }
Usage:Factory.CreateFruit(57.3f, Apple.CreateOne);
- erstellt und gibt einApple
, with zurückweight=57.3f
.Sie können keinen parametrisierten Konstruktor verwenden. Sie können einen parameterlosen Konstruktor verwenden, wenn Sie eine "
where T : new()
" Einschränkung haben.Es ist ein Schmerz, aber so ist das Leben :(
Dies ist eines der Dinge, die ich mit "statischen Schnittstellen" ansprechen möchte . Sie können T dann so einschränken, dass es statische Methoden, Operatoren und Konstruktoren enthält, und diese dann aufrufen.
quelle
Ja; Ändern Sie Ihren Aufenthaltsort:
Dies funktioniert jedoch nur mit parameterlosen Konstruktoren. Sie müssen über andere Möglichkeiten zum Festlegen Ihrer Eigenschaft verfügen (Festlegen der Eigenschaft selbst oder ähnliches).
quelle
Einfachste Lösung
Activator.CreateInstance<T>()
quelle
Wie Jon betonte, ist dies das Leben, um einen nicht parameterlosen Konstruktor einzuschränken. Eine andere Lösung besteht jedoch darin, ein Fabrikmuster zu verwenden. Dies ist leicht einschränkbar
Eine weitere Option ist die Verwendung eines funktionalen Ansatzes. Übergeben Sie eine Werksmethode.
quelle
Sie können dies mithilfe der Reflexion tun:
BEARBEITEN: Konstruktor == Nullprüfung hinzugefügt.
EDIT: Eine schnellere Variante mit einem Cache:
quelle
Als Ergänzung zum Vorschlag von user1471935:
Um eine generische Klasse mithilfe eines Konstruktors mit einem oder mehreren Parametern zu instanziieren, können Sie jetzt die Activator-Klasse verwenden.
Die Liste der Objekte sind die Parameter, die Sie angeben möchten. Laut Microsoft :
Es gibt auch eine generische Version von CreateInstance (
CreateInstance<T>()
), aber diese erlaubt es Ihnen auch nicht, Konstruktorparameter anzugeben .quelle
Ich habe diese Methode erstellt:
Ich benutze das so:
Code:
quelle
Kürzlich bin ich auf ein sehr ähnliches Problem gestoßen. Ich wollte nur unsere Lösung mit Ihnen allen teilen. Ich wollte eine Instanz von a
Car<CarA>
aus einem json-Objekt erstellen, das eine Aufzählung hatte:quelle
Mit hoher Leistung ist es immer noch möglich, Folgendes zu tun:
und
Die relevanten Klassen müssen dann von dieser Schnittstelle abgeleitet und entsprechend initialisiert werden. Bitte beachten Sie, dass dieser Code in meinem Fall Teil einer umgebenden Klasse ist, die bereits <T> als generischen Parameter hat. R ist in meinem Fall auch eine schreibgeschützte Klasse. IMO hat die öffentliche Verfügbarkeit von Initialize () -Funktionen keinen negativen Einfluss auf die Unveränderlichkeit. Der Benutzer dieser Klasse könnte ein anderes Objekt einfügen, dies würde jedoch die zugrunde liegende Sammlung nicht ändern.
quelle