Jeśli BaseFruit
ma konstruktora, który akceptuje int weight
, czy mogę utworzyć egzemplarz owocu za pomocą ogólnej metody takiej jak ta?
public void AddFruit<T>()where T: BaseFruit{
BaseFruit fruit = new T(weight); /*new Apple(150);*/
fruit.Enlist(fruitManager);
}
Przykład został dodany za komentarzami. Wydaje się, że mogę to zrobić tylko wtedy, gdy podam BaseFruit
konstruktor bez parametrów, a następnie wypełnię wszystko przez zmienne składowe. W moim prawdziwym kodzie (nie o owocach) jest to raczej niepraktyczne.
-Aktualizacja-
Wygląda więc na to, że nie można w żaden sposób rozwiązać ograniczeń. Z odpowiedzi istnieją trzy kandydujące rozwiązania:
- Wzór fabryczny
- Odbicie
- Aktywator
Wydaje mi się, że odbicie jest najmniej czyste, ale nie mogę się zdecydować między pozostałymi dwoma.
Odpowiedzi:
Dodatkowo prostszy przykład:
Zauważ, że użycie nowego ograniczenia () na T służy tylko do sprawdzania przez kompilator publicznego konstruktora bez parametrów w czasie kompilacji, rzeczywistym kodem użytym do utworzenia typu jest klasa Activator.
Musisz upewnić się co do konkretnego konstruktora, a tego rodzaju wymaganiem może być zapach kodu (a raczej coś, czego powinieneś po prostu unikać w obecnej wersji na c #).
źródło
new object[] { weight }
.CreateInstance
jest zadeklarowany z parametramipublic static object CreateInstance(Type type, params object[] args)
, więc możesz to zrobićreturn (T) Activator.CreateInstance(typeof(T), weight);
. Jeśli istnieje wiele parametrów, przekaż je jako osobne argumenty. Tylko jeśli masz już konstruowaną listę parametrów, powinieneś zadać sobie trud, aby ją przekonwertowaćobject[]
i przekazać doCreateInstance
.Func
która tworzy nową instancję. Załóżmy, żeApple
użycie konstruktora jestnew Apple(wgt)
. Następnie dodaj doApple
klasy tę definicję:static Func<float, Fruit> CreateOne { get; } = (wgt) => new Apple(wgt);
W ustawieniu fabrycznympublic static Fruit CreateFruitGiven(float weight, Func<float, Fruit> createOne) { return createOne(weight); }
Użycie:Factory.CreateFruit(57.3f, Apple.CreateOne);
- który tworzy i zwracaApple
, zweight=57.3f
.Nie można użyć żadnego sparametryzowanego konstruktora. Możesz użyć konstruktora bez parametrów, jeśli masz
where T : new()
ograniczenie.To ból, ale takie jest życie :(
Jest to jedna z rzeczy, które chciałbym rozwiązać za pomocą „interfejsów statycznych” . Będziesz wtedy mógł ograniczyć T do włączenia metod statycznych, operatorów i konstruktorów, a następnie wywołać je.
źródło
Tak; zmień swoje miejsce pobytu:
Działa to jednak tylko z konstruktorami bez parametrów . Będziesz musiał mieć inne sposoby na ustawienie swojej właściwości (ustawienie samej nieruchomości lub czegoś podobnego).
źródło
Najprostsze rozwiązanie
Activator.CreateInstance<T>()
źródło
Jak zauważył Jon, jest to życie w ograniczaniu nieparametrycznego konstruktora. Jednak innym rozwiązaniem jest użycie wzorca fabrycznego. Można to łatwo ograniczyć
Jeszcze inną opcją jest zastosowanie podejścia funkcjonalnego. Przekaż metodę fabryczną.
źródło
Możesz to zrobić za pomocą odbicia:
EDYCJA: Dodano konstruktor == kontrola zerowa.
EDYCJA: Szybszy wariant wykorzystujący pamięć podręczną:
źródło
Jako dodatek do sugestii użytkownika1471935:
Aby utworzyć instancję klasy ogólnej za pomocą konstruktora z co najmniej jednym parametrem, możesz teraz użyć klasy Activator.
Lista obiektów to parametry, które chcesz podać. Według Microsoft :
Istnieje również ogólna wersja CreateInstance (
CreateInstance<T>()
), ale ta również nie pozwala na podanie parametrów konstruktora.źródło
Stworzyłem tę metodę:
Używam tego w ten sposób:
Kod:
źródło
Ostatnio natknąłem się na bardzo podobny problem. Chciałem tylko podzielić się z Wami naszym rozwiązaniem. Chciałem utworzyć instancję obiektu
Car<CarA>
z obiektu Json, który miał wyliczenie:źródło
Nadal jest to możliwe, przy wysokiej wydajności, wykonując następujące czynności:
i
Odpowiednie klasy muszą następnie wyprowadzić się z tego interfejsu i odpowiednio zainicjować. Proszę zauważyć, że w moim przypadku ten kod jest częścią otaczającej klasy, która ma już <T> jako parametr ogólny. R w moim przypadku jest także klasą tylko do odczytu. IMO, publiczna dostępność funkcji Initialize () nie ma negatywnego wpływu na niezmienność. Użytkownik tej klasy może umieścić inny obiekt, ale nie zmodyfikuje to podstawowej kolekcji.
źródło