Po nieudanej kompilacji czegoś podobnego do następującego:
public class Gen<T> where T : System.Array
{
}
z błędem
Ograniczenie nie może być klasą specjalną `` System.Array ''
Zacząłem się zastanawiać, czym właściwie są „specjalne zajęcia”?
Ludziom często wydaje się, że popełniają ten sam rodzaj błędu, gdy określają System.Enum
w ogólnym ograniczeniu. Mam te same rezultaty System.Object
, System.Delegate
, System.MulticastDelegate
i System.ValueType
też.
Czy jest ich więcej? Nie mogę znaleźć żadnych informacji o „klasach specjalnych” w C #.
Co jest takiego specjalnego w tych klasach, że nie możemy ich używać jako ogólnego ograniczenia typu?
c#
class
generics
generic-constraints
Mennice97
źródło
źródło
System.Object
jest to „klasa specjalna”, ponieważ jest to poprawne: ale nadal jest „klasą specjalną”.public class X : System.Object { }
System.Object
Odpowiedzi:
Z kodu źródłowego Roslyn wygląda to na listę typów zakodowanych na stałe:
Źródło: Binder_Constraints.cs IsValidConstraintType
Znalazłem to za pomocą wyszukiwania GitHub: „Ograniczenie nie może być klasą specjalną”
źródło
CS0702
.object
), a przynajmniej ma to z tym coś wspólnego.where T : Array
Pozwoliłoby również na zaliczenie testu jako T, co prawdopodobnie nie jest tym, czego chce większość ludzi.Znalazłem komentarz Jona Skeeta z 2008 r. Dotyczący podobnego pytania: Dlaczego to
System.Enum
ograniczenie nie jest obsługiwane.Wiem, że to trochę nie na temat , ale zapytał o to Erica Lipperta (zespół C #), a oni udzielili takiej odpowiedzi:
źródło
Według MSDN jest to statyczna lista klas:
Błąd kompilatora CS0702
Ograniczenie nie może być specjalnym identyfikatorem klasy Następujących typów nie można używać jako ograniczeń:
źródło
System.MulticastDelegate
na liście?Zgodnie ze specyfikacją języka C # 4.0 (kodowane: [10.1.5] ograniczenia parametrów typu) mówi się o dwóch rzeczach:
Podczas definiowania klasy ogólnej można zastosować ograniczenia do rodzajów typów, których kod klienta może używać dla argumentów typu podczas tworzenia wystąpienia klasy. Jeśli kod klienta próbuje utworzyć wystąpienie klasy przy użyciu typu, który nie jest dozwolony przez ograniczenie, wynikiem jest błąd w czasie kompilacji. Te ograniczenia nazywane są ograniczeniami. Ograniczenia są określane przy użyciu kontekstowego słowa kluczowego where. Jeśli chcesz ograniczyć typ ogólny, aby był typem referencyjnym, użyj: class.
Dzięki temu typ ogólny nie będzie typem wartości, takim jak int lub struct itp.
Ponadto ograniczenie nie może być specjalnym „identyfikatorem” klasy Następujących typów nie można używać jako ograniczeń:
źródło
Istnieją pewne klasy w Framework, które skutecznie przekazują specjalne cechy wszystkim typom z nich pochodnym, ale same nie posiadają tych cech . Sam CLR nie nakłada zakazu używania tych klas jako ograniczeń, ale typy ogólne ograniczone do nich nie uzyskałyby niedziedziczonych cech, tak jak typy konkretne. Twórcy C # zdecydowali, że ponieważ takie zachowanie może dezorientować niektórych ludzi, a nie dostrzegli w nim żadnej użyteczności, powinni raczej zakazać takich ograniczeń, niż pozwolić im zachowywać się tak, jak robią w CLR.
Gdyby na przykład wolno było pisać
void CopyArray<T>(T dest, T source, int start, int count)
:; można by przekazaćdest
isource
do metod, które oczekują argumentu typuSystem.Array
; ponadto, można by uzyskać walidację w czasie kompilacji, żedest
isource
były zgodne typy tablic, ale nie można uzyskać dostępu do elementów tablicy za pomocą[]
operatora.Niemożność użycia
Array
jako ograniczenia jest przeważnie dość łatwa do obejścia, ponieważvoid CopyArray<T>(T[] dest, T[] source, int start, int count)
będzie działać w prawie wszystkich sytuacjach, w których zadziała pierwsza metoda. Ma jednak pewną wadę: pierwsza metoda działałaby w scenariuszu, w którym jeden lub oba argumenty były typu,System.Array
podczas odrzucania przypadków, w których argumenty są niezgodnymi typami tablicowymi; dodanie przeciążenia, w którym oba argumenty byłyby typuSystem.Array
, spowodowałoby, że kod zaakceptowałby dodatkowe przypadki, które powinien zaakceptować, ale także błędnie zaakceptowałoby przypadki, których nie powinien.Uważam, że decyzja o zdelegalizowaniu większości specjalnych ograniczeń jest irytująca. Jedynym, który miałby zerowe znaczenie semantyczne, byłoby
System.Object
[ponieważ gdyby było to legalne jako ograniczenie, wszystko by je spełniało].System.ValueType
prawdopodobnie nie byłby zbyt przydatny, ponieważ referencje typuValueType
tak naprawdę nie mają wiele wspólnego z typami wartości, ale prawdopodobnie mogą mieć jakąś wartość w przypadkach obejmujących odbicie. ObaSystem.Enum
iSystem.Delegate
miałyby pewne rzeczywiste zastosowania, ale ponieważ twórcy C # nie pomyśleli o nich, są wyjęci spod prawa bez dobrego powodu.źródło
Następujące elementy można znaleźć w środowisku CLR za pośrednictwem C # 4th Edition:
Podstawowe ograniczenia
Parametr typu może określać zero ograniczeń podstawowych lub jedno ograniczenie podstawowe. Głównym ograniczeniem może być typ referencyjny, który identyfikuje klasę, która nie jest zapieczętowana. Nie można określić jednego z następujących specjalnych typów odwołań: System.Object , System.Array , System.Delegate , System.MulticastDelegate , System.ValueType , System.Enum lub System.Void . Określając ograniczenie typu referencyjnego, obiecujesz kompilatorowi, że określony argument typu będzie tego samego typu lub typu pochodzącego z typu ograniczenia.
źródło
System.Array
,System.Delegate
,System.MulticastDelegate
,System.Enum
, lubSystem.ValueType
. Ponadto deklaracji klasy ogólnej nie można używaćSystem.Attribute
jako bezpośredniej ani pośredniej klasy bazowej.Nie sądzę, żeby istniała jakakolwiek oficjalna definicja „klas specjalnych” / „typów specjalnych”.
Możesz pomyśleć o nich jako o typach, których nie można używać z semantycznymi typami „zwykłymi”:
PS dodałbym
System.Void
do listy.źródło
System.Void
daje zupełnie inny błąd, gdy jest używany jako ogólne ograniczenie =)void
jest bardzo szczególne. :)System.Array
może używać metod takich jakArray.Copy
przenoszenie danych z jednego do drugiego; kod z parametrami typu ograniczonego doSystem.Delegate
będzie mógłDelegate.Combine
na nich użyć i rzutować wynik na właściwy typ . Efektywne wykorzystanie ogólnego znanego typuEnum
będzie używać Reflection raz dla każdego takiego typu, aleHasAnyFlag
metoda ogólna może być 10 razy szybsza niż metoda nieogólna.