Mam więc tę klasę:
public class Foo<T> where T : ???
{
private T item;
public bool IsNull()
{
return item == null;
}
}
Teraz szukam ograniczenia typu, które pozwala mi użyć wszystkiego jako parametru typu, który może być null
. Oznacza to wszystkie typy odwołań, a także wszystkie Nullable
( T?
) typy:
Foo<String> ... = ...
Foo<int?> ... = ...
powinno być możliwe.
Używanie class
jako ograniczenia typu pozwala mi używać tylko typów referencyjnych.
Dodatkowe informacje:
Piszę aplikację potoków i filtrów i chcę użyć null
odniesienia jako ostatniego elementu, który przechodzi do potoku, aby każdy filtr mógł ładnie się zamknąć, wyczyścić itp.
IFoo<T>
jako typu roboczego i tworzyć instancje za pomocą metody fabrycznej? To mogłoby zadziałać.Odpowiedzi:
Jeśli chcesz przeprowadzić kontrolę środowiska wykonawczego w konstruktorze Foo zamiast sprawdzać w czasie kompilacji, możesz sprawdzić, czy typ nie jest typem referencyjnym lub dopuszczającym wartość null, i zgłosić wyjątek, jeśli tak jest.
Zdaję sobie sprawę, że samo sprawdzenie działania może być niedopuszczalne, ale na wszelki wypadek:
Następnie kompilowany jest poniższy kod, ale ostatnia (
foo3
) zgłasza wyjątek w konstruktorze:źródło
static bool isValidType
pole, które ustawiłeś w konstruktorze statycznym, a następnie po prostu sprawdź tę flagę w konstruktorze instancji i wyrzuć, jeśli jest to nieprawidłowy typ, aby nie wykonywać wszystkich czynności sprawdzających za każdym razem, gdy konstruujesz instancja. Często używam tego wzoru.Nie wiem, jak zaimplementować odpowiednik OR w typach ogólnych. Mogę jednak zaproponować użycie domyślnego słowa kluczowego w celu utworzenia wartości null dla typów dopuszczających wartość null i wartości 0 dla struktur:
Możesz również zaimplementować swoją wersję Nullable:
Przykład:
źródło
Natknąłem się na ten problem w prostszym przypadku, gdy potrzebowałem ogólnej metody statycznej, która mogłaby przyjmować wszystko, co „dopuszcza wartość zerową” (typy referencyjne lub wartości Nullables), co doprowadziło mnie do tego pytania bez zadowalającego rozwiązania. Tak więc wymyśliłem własne rozwiązanie, które było stosunkowo łatwiejsze do rozwiązania niż zadane pytanie PO, po prostu mając dwie przeciążone metody, jedną, która przyjmuje a
T
i ma ograniczenie,where T : class
a drugą, która przyjmuje aT?
i mawhere T : struct
.Wtedy zdałem sobie sprawę, że rozwiązanie można również zastosować do tego problemu, aby stworzyć rozwiązanie, które można sprawdzić w czasie kompilacji, ustawiając konstruktor jako prywatny (lub chroniony) i używając statycznej metody fabryki:
Teraz możemy to wykorzystać w ten sposób:
Jeśli potrzebujesz konstruktora bez parametrów, nie uzyskasz subtelności przeciążenia, ale nadal możesz zrobić coś takiego:
I użyj tego w ten sposób:
To rozwiązanie ma kilka wad, jedną z nich jest to, że możesz preferować używanie „nowego” do konstruowania obiektów. Innym jest to, że nie będą mogli używać
Foo<T>
jako argumentu typu rodzajowego dla typu przymusu czegoś podobnego:where TFoo: new()
. Na koniec jest trochę dodatkowego kodu, którego potrzebujesz tutaj, który zwiększyłby się, szczególnie jeśli potrzebujesz wielu przeciążonych konstruktorów.źródło
Jak wspomniano, nie można tego sprawdzić w czasie kompilacji. Brakuje ogólnych ograniczeń w .NET i nie obsługują one większości scenariuszy.
Uważam jednak, że jest to lepsze rozwiązanie do sprawdzania w czasie wykonywania. Można go zoptymalizować w czasie kompilacji JIT, ponieważ obie są stałymi.
źródło
Takie ograniczenie typu nie jest możliwe. Zgodnie z dokumentacją ograniczeń typu nie ma ograniczenia, które przechwytuje zarówno typy dopuszczające wartość null, jak i typy referencyjne. Ponieważ ograniczenia można łączyć tylko w koniunkcji, nie ma możliwości utworzenia takiego ograniczenia przez kombinację.
Możesz jednak, dla swoich potrzeb, wrócić do parametru typu nieograniczonego, ponieważ zawsze możesz sprawdzić == null. Jeśli typ jest typem wartości, sprawdzenie zawsze da wynik fałszywy. Wtedy prawdopodobnie otrzymasz ostrzeżenie R # „Możliwe porównanie typu wartości z wartością null”, które nie jest krytyczne, o ile semantyka jest dla Ciebie odpowiednia.
Alternatywą może być użycie
zamiast sprawdzania wartości null, ponieważ default (T), gdzie T: class jest zawsze null. Oznacza to jednak, że nie można odróżnić pogody, a wartość nie dopuszczająca wartości null nigdy nie została jawnie ustawiona lub po prostu została ustawiona na wartość domyślną.
źródło
używam
źródło
źródło