Podczas dodawania do listy próbuję utworzyć nowy obiekt typu T za pomocą jego konstruktora.
Otrzymuję błąd kompilacji: Komunikat o błędzie to:
„T”: nie można podać argumentów podczas tworzenia instancji zmiennej
Ale moje klasy mają argument konstruktorski! Jak mogę to zrobić?
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
FUNT.
źródło
źródło
Odpowiedzi:
Aby utworzyć instancję typu ogólnego w funkcji, musisz ograniczyć ją flagą „new”.
Działa to jednak tylko wtedy, gdy chcesz wywołać konstruktor, który nie ma parametrów. Nie w tym przypadku. Zamiast tego musisz podać inny parametr, który pozwala na tworzenie obiektu na podstawie parametrów. Najłatwiejsza jest funkcja.
Możesz to tak nazwać
źródło
T result = thunk == null ? new T() : thunk();
Zaletą tego jest dla mnie utrwalenie logikiT
tworzenia w jednym miejscu zamiast czasem tworzeniaT
wewnątrz, a czasem poza metodą.w .Net 3.5 i po tym, jak można użyć klasy aktywatora:
źródło
Activator.CreateInstance
na tę jedną rzecz, będzie wyglądać, jakby Twój konstruktor w ogóle nie był używany, a ktoś może spróbować „wyczyścić” i usunąć go (aby spowodować błąd w czasie wykonywania jakiś losowy czas w przyszłości). Możesz rozważyć dodanie funkcji zastępczej, w której używasz tego konstruktora, aby uzyskać błąd kompilacji, jeśli spróbujesz go usunąć.Ponieważ nikt nie zadał sobie trudu opublikowania odpowiedzi „Refleksja” (którą osobiście uważam za najlepszą odpowiedź), oto:
Edycja: Ta odpowiedź jest nieaktualna z powodu Activator.CreateInstance .NET 3.5, jednak nadal jest przydatna w starszych wersjach .NET.
źródło
Inicjator obiektu
Jeśli twój konstruktor z parametrem nie robi nic poza ustawieniem właściwości, możesz to zrobić w C # 3 lub lepiej, używając inicjatora obiektu zamiast wywoływać konstruktor (co jest niemożliwe, jak wspomniano):
Korzystając z tego, zawsze możesz umieścić dowolną logikę konstruktora w domyślnym (pustym) konstruktorze.
Activator.CreateInstance ()
Alternatywnie możesz wywołać Activator.CreateInstance () w następujący sposób:
Należy pamiętać, że Activator.CreateInstance może mieć pewne narzuty wydajności , których można uniknąć, jeśli szybkość wykonywania jest najwyższym priorytetem, a inna opcja jest możliwa do utrzymania.
źródło
T
to ochronie niezmienników (biorąc pod uwagę, żeT
ma> 0 zależności lub wymagane wartości, możesz teraz tworzyć instancje,T
które są w stanie nieprawidłowym / nieużywalnym. chyba żeT
jest to coś tak prostego jak DTO och viewmodel, powiedziałbym, że unikaj tego.Bardzo stare pytanie, ale nowa odpowiedź ;-)
Wersja ExpressionTree : (Myślę, że najszybsze i najczystsze rozwiązanie)
Jak powiedział Welly Tambunan , „możemy również użyć drzewa wyrażeń do zbudowania obiektu”
Spowoduje to wygenerowanie „konstruktora” (funkcji) dla podanego typu / parametrów. Zwraca delegata i akceptuje typy parametrów jako tablicę obiektów.
Oto on:
Przykład MyClass:
Stosowanie:
Kolejny przykład: przekazywanie typów jako tablic
Debuguj widok wyrażenia
Jest to równoważne z generowanym kodem:
Mały minus
Wszystkie parametry typów wartości są umieszczane w ramkach, gdy są przekazywane jak tablica obiektów.
Prosty test wydajności:
Wyniki:
Używanie
Expressions
jest +/- 8 razy szybsze niż WywołanieConstructorInfo
i +/- 20 razy szybsze niż używanieActivator
źródło
var myConstructor = CreateConstructor(typeof(MyClass<int>), typeof(int));
to działa dobrze. Nie wiemTo nie zadziała w twojej sytuacji. Możesz jedynie określić ograniczenie, że ma pusty konstruktor:
Możesz użyć zastrzyku właściwości, definiując ten interfejs:
Następnie możesz zmienić swoją metodę na:
Inną alternatywą jest
Func
metoda opisana przez JaredPar.źródło
Musisz dodać gdzie T: new (), aby poinformować kompilator, że T zapewnia domyślny konstruktor.
źródło
Jeśli chcesz po prostu zainicjować pole członka lub właściwość za pomocą parametru konstruktora, w C #> = 3 możesz to zrobić bardzo łatwo:
To samo powiedział Garry Shutler, ale chciałbym dodać dodatkową notatkę.
Oczywiście możesz użyć sztuczki właściwości, aby zrobić więcej rzeczy niż tylko ustawienie wartości pola. Właściwość „set ()” może wyzwalać dowolne przetwarzanie potrzebne do ustawienia powiązanych pól i wszelkie inne potrzeby samego obiektu, w tym sprawdzenie, czy pełna inicjalizacja ma nastąpić przed użyciem obiektu, symulując pełną konstrukcję ( tak, jest to brzydkie obejście, ale pokonuje nowe () ograniczenie M $).
Nie mogę się upewnić, czy jest to planowana dziura, czy przypadkowy efekt uboczny, ale działa.
To bardzo zabawne, jak stwardnienie rozsiane dodaje nowe funkcje do języka i wydaje się, że nie wykonuje pełnej analizy skutków ubocznych. Cała ogólna rzecz jest dobrym dowodem na to ...
źródło
Stwierdziłem, że pojawia się błąd „nie można podać argumentów podczas tworzenia wystąpienia parametru typu T”, więc musiałem to zrobić:
źródło
Jeśli masz dostęp do klasy, z której będziesz korzystać, możesz skorzystać z tego podejścia, którego użyłem.
Utwórz interfejs, który ma alternatywnego twórcę:
Twórz zajęcia z pustym twórcą i zaimplementuj tę metodę:
Teraz użyj ogólnych metod:
Jeśli nie masz dostępu, zawiń klasę docelową:
źródło
Jest to trochę paskudne, a kiedy mówię trochę paskudne, mam na myśli zbuntowane, ale zakładając, że możesz dostarczyć sparametryzowany typ pustemu konstruktorowi:
Skutecznie pozwoli ci zbudować obiekt ze sparametryzowanego typu z argumentem. W tym przypadku zakładam, że konstruktor, którego chcę, ma jeden argument typu
object
. Tworzymy atrapę instancji T, używając dozwolonego pustego konstruktora, a następnie używamy odbicia, aby uzyskać jeden z jej innych konstruktorów.źródło
Czasami używam podejścia, które przypomina odpowiedzi przy użyciu wstrzykiwania właściwości, ale utrzymuje czystość kodu. Zamiast mieć klasę bazową / interfejs z zestawem właściwości, zawiera on tylko (wirtualną) metodę Initialize () - która działa jak „konstruktor biedaka”. Następnie możesz pozwolić każdej klasie zająć się własną inicjalizacją, tak jak zrobiłby to konstruktor, co dodaje także wygodny sposób obsługi łańcuchów dziedziczenia.
Jeśli często znajduję się w sytuacjach, w których chcę, aby każda klasa w łańcuchu zainicjowała swoje unikalne właściwości, a następnie wywołała metodę nadrzędną Initialize () - metodę, która z kolei inicjuje unikalne właściwości rodzica i tak dalej. Jest to szczególnie przydatne w przypadku różnych klas, ale o podobnej hierarchii, na przykład obiektów biznesowych odwzorowanych na / z DTO: s.
Przykład, który używa wspólnego słownika do inicjalizacji:
źródło
Jeśli wszystko, czego potrzebujesz, to konwersja z ListItem na typ T, możesz zaimplementować tę konwersję w klasie T jako operator konwersji.
źródło
Uważam, że musisz ograniczyć T za pomocą instrukcji where, aby zezwolić tylko na obiekty z nowym konstruktorem.
Teraz akceptuje wszystko, w tym przedmioty bez niego.
źródło