Zgodnie z dokumentacją ==
operatora w MSDN ,
W przypadku predefiniowanych typów wartości operator równości (==) zwraca wartość true, jeśli wartości jego argumentów są równe, w przeciwnym razie false. W przypadku typów odwołań innych niż łańcuch == zwraca wartość true, jeśli dwa operandy odnoszą się do tego samego obiektu. Dla typu ciągu == porównuje wartości ciągów. Zdefiniowane przez użytkownika typy wartości mogą przeciążać operatora == (patrz operator). Podobnie mogą być typy referencyjne zdefiniowane przez użytkownika, chociaż domyślnie == zachowuje się tak, jak opisano powyżej, zarówno dla typów predefiniowanych, jak i zdefiniowanych przez użytkownika.
Dlaczego więc ten fragment kodu się nie kompiluje?
bool Compare<T>(T x, T y) { return x == y; }
Pojawia się błąd Operator „==” nie może być stosowany do operandów typu „T” i „T” . Zastanawiam się, dlaczego, skoro o ile rozumiem, ==
operator jest predefiniowany dla wszystkich typów?
Edycja: Dzięki wszystkim. Z początku nie zauważyłem, że stwierdzenie dotyczy tylko typów referencji. Pomyślałem też, że porównanie bit po bicie jest dla wszystkich typów wartości, co wiem teraz jest nie poprawny.
Ale w przypadku, gdy korzystam z typu odniesienia, czy ==
operator użyłby wstępnie zdefiniowanego porównania odniesienia, czy użyłby przeciążonej wersji operatora, jeśli typ byłby zdefiniowany?
Edycja 2: Dzięki próbom i błędom dowiedzieliśmy się, że ==
operator użyje predefiniowanego porównania referencyjnego, gdy użyje nieograniczonego typu ogólnego. Właściwie kompilator użyje najlepszej metody, jaką może znaleźć dla argumentu typu ograniczonego, ale nie będzie dalej szukał. Na przykład poniższy kod zawsze będzie drukowany true
, nawet gdy Test.test<B>(new B(), new B())
zostanie wywołany:
class A { public static bool operator==(A x, A y) { return true; } }
class B : A { public static bool operator==(B x, B y) { return false; } }
class Test { void test<T>(T a, T b) where T : A { Console.WriteLine(a == b); } }
źródło
==
nie można używać dwóch operandów tego samego typu. Dotyczy tostruct
typów (z wyjątkiem typów „predefiniowanych”), które nie przeciążająoperator ==
. Jako prosty przykład spróbuj tego:var map = typeof(string).GetInterfaceMap(typeof(ICloneable)); Console.WriteLine(map == map); /* compile-time error */
var kvp1 = new KeyValuePair<int, int>(); var kvp2 = kvp1;
, nie można sprawdzić,kvp1 == kvp2
ponieważKeyValuePair<,>
jest strukturą, nie jest typem wstępnie zdefiniowanym w języku C # i nie powoduje przeciążeniaoperator ==
. Podany jest jednak przykład, zavar li = new List<int>(); var e1 = li.GetEnumerator(); var e2 = e1;
pomocą którego nie można tego zrobiće1 == e2
(tutaj mamy zagnieżdżoną strukturęList<>.Enumerator
(wywoływaną"List`1+Enumerator[T]"
przez środowisko wykonawcze), która się nie przeciąża==
).bool
zvoid
...Odpowiedzi:
„... domyślnie == zachowuje się tak, jak opisano powyżej, zarówno dla predefiniowanych, jak i zdefiniowanych przez użytkownika typów referencji.”
Typ T niekoniecznie jest typem referencyjnym, więc kompilator nie może przyjąć tego założenia.
Jednak to się skompiluje, ponieważ jest bardziej jednoznaczne:
Postępuj zgodnie z dodatkowym pytaniem: „Ale w przypadku, gdy używam typu odniesienia, operator == użyłby wstępnie zdefiniowanego porównania odniesienia, czy użyłby przeciążonej wersji operatora, jeśli typ zdefiniowałby?”
Myślałem, że == w Generics użyłby przeciążonej wersji, ale poniższy test pokazuje inaczej. Ciekawe ... Chciałbym wiedzieć, dlaczego! Jeśli ktoś wie, udostępnij.
Wynik
Inline: Przeciążony == wywołany
Rodzajowy:
Naciśnij dowolny klawisz, aby kontynuować . . .
Kontynuacja 2
Chcę podkreślić, że zmieniając moją metodę porównywania na
powoduje wywołanie przeciążonego operatora ==. Wydaje mi się, że bez podania typu (jako miejsca ) kompilator nie może wywnioskować, że powinien użyć przeciążonego operatora ... choć sądzę, że będzie miał wystarczającą ilość informacji, aby podjąć decyzję nawet bez podania typu.
źródło
Equals
metodzie (nie w==
operatorze).==
typami rodzajowymiT
iT
, stwierdzamy najlepsze przeciążenie, biorąc pod uwagę ograniczenia, jakie niesieT
(istnieje specjalna zasada, że nigdy nie określi dla tego typu wartości (co dałoby bezsensowny wynik), stąd musi być niektóre ograniczenia gwarantujące, że jest to typ referencyjny). W swojej śledzić 2 , jeśli przyjeżdża się zDerivedTest
przedmiotami, aDerivedTest
wywodziTest
ale wprowadza nowy przeciążenia==
, będziesz miał „problem” ponownie. To, które przeciążenie się nazywa, jest „wypalane” do IL w czasie kompilacji.Jak powiedzieli inni, będzie działać tylko wtedy, gdy T jest ograniczony do typu odniesienia. Bez żadnych ograniczeń można porównać z wartością null, ale tylko z wartością null - i to porównanie zawsze będzie fałszywe dla typów wartości, które nie mają wartości zerowych.
Zamiast wywoływać Equals, lepiej użyć
IComparer<T>
- a jeśli nie masz więcej informacji,EqualityComparer<T>.Default
to dobry wybór:Oprócz czegokolwiek innego, pozwala to uniknąć boksu / castingu.
źródło
Ogólnie rzecz biorąc,
EqualityComparer<T>.Default.Equals
powinien wykonywać pracę ze wszystkim, co implementujeIEquatable<T>
lub ma sensownąEquals
implementację.Jeśli jednak
==
iEquals
z jakiegoś powodu są wdrażane inaczej, moja praca nad operatorami rodzajowymi powinna być użyteczna; wspiera operatora wersje (między innymi):źródło
Tak wiele odpowiedzi, a żadna z nich nie wyjaśnia DLACZEGO? (o co wyraźnie poprosił Giovanni) ...
Generyczne .NET nie działają jak szablony C ++. W szablonach C ++ rozwiązywanie przeciążenia występuje po poznaniu rzeczywistych parametrów szablonu.
W generycznych .NET (w tym C #) rozwiązywanie przeciążeń występuje bez znajomości rzeczywistych parametrów ogólnych. Jedyne informacje, których kompilator może użyć do wybrania funkcji do wywołania, pochodzą z ograniczeń typu w parametrach ogólnych.
źródło
==
działa dla wszystkich typów, niezależnie od tego, czy są to typy referencyjne, czy typy wartości. To powinno być pytanie, na które nie sądzę, że odpowiedziałeś.==
nie działa dla wszystkich typów wartości. Co ważniejsze, nie ma tego samego znaczenia dla wszystkich typów, więc kompilator nie wie, co z tym zrobić.==
. Czy możesz również dołączyć tę część do swojej odpowiedzi, jak sądzę, że to jest główny punkt tutajKompilacja nie może wiedzieć, że T nie może być strukturą (typ wartości). Więc musisz powiedzieć, że może to być tylko typ referencyjny, myślę:
Jest tak, ponieważ jeśli T może być typem wartości, mogą istnieć przypadki, w których
x == y
byłby źle sformułowany - w przypadkach, gdy typ nie ma zdefiniowanego operatora ==. To samo stanie się z tym, co jest bardziej oczywiste:To też się nie udaje, ponieważ można przekazać typ T, który nie miałby funkcji foo. C # zmusza cię do upewnienia się, że wszystkie możliwe typy zawsze mają funkcję foo. Robi to klauzula where.
źródło
Wygląda na to, że bez ograniczenia klasy:
Należy zdawać sobie sprawę, że będąc
class
ograniczonymEquals
w==
operatora dziedziczyObject.Equals
, podczas gdy struktura zastępujeValueType.Equals
.Uwaga:
podaje również ten sam błąd kompilatora.
Jak dotąd nie rozumiem, dlaczego kompilator odrzuca porównanie operatora równości typu wartości. Wiem jednak na pewno, że to działa:
źródło
Object.Equals
ale testuje równość odniesienia. Na przykładCompare("0", 0.ToString())
zwróciłoby wartość false, ponieważ argumenty byłyby odniesieniami do odrębnych ciągów, z których oba mają zero jako jedyny znak.NullReferenceException
może się zdarzyć.W moim przypadku chciałem przetestować jednostkę operatora równości. Potrzebowałem wywołać kod w ramach operatorów równości bez jawnego ustawiania typu ogólnego. Porady dla
EqualityComparer
nie były przydatne jako metodaEqualityComparer
nazywanaEquals
, ale nie były operatorem równości.Oto jak mam to pracować z typami rodzajowymi, budując
LINQ
. Wywołuje odpowiedni kod==
i!=
operatory:źródło
Jest to wpis MSDN Connect dla tego tutaj
Odpowiedź Alexa Turnera zaczyna się od:
źródło
Jeśli chcesz się upewnić, że operatorzy niestandardowego typu są nazywani, możesz to zrobić przez odbicie. Wystarczy pobrać typ za pomocą parametru ogólnego i pobrać MethodInfo dla żądanego operatora (np. Op_Equality, op_Inequality, op_LessThan ...).
Następnie uruchom operator za pomocą metody Invoke MethodInfo i przekaż obiekty jako parametry.
Spowoduje to wywołanie przeciążonego operatora, a nie tego określonego przez ograniczenia nałożone na parametr ogólny. Może to nie być praktyczne, ale może się przydać do testowania jednostkowego operatorów przy użyciu ogólnej klasy bazowej, która zawiera kilka testów.
źródło
Napisałem następującą funkcję, patrząc na najnowszy msdn. Może łatwo porównywać dwa obiekty
x
iy
:źródło
return ((IComparable)(x)).CompareTo(y) <= 0;
Powyższe zadziała, ponieważ == jest załatwiane w przypadku typów referencyjnych zdefiniowanych przez użytkownika.
W przypadku typów wartości == można zastąpić. W takim przypadku należy również zdefiniować „! =”.
Myślę, że może to być przyczyną, nie pozwala na ogólne porównanie przy użyciu „==”.
źródło
==
Znak jest używany dla dwóch różnych operatorów. Jeśli dla danych typów argumentów istnieje kompatybilne przeciążenie operatora równości, to przeciążenie zostanie zastosowane. W przeciwnym razie, jeśli oba operandy są typami referencji, które są ze sobą kompatybilne, zostanie użyte porównanie referencji. Zauważ, że wCompare
powyższej metodzie kompilator nie może powiedzieć, że pierwsze znaczenie ma zastosowanie, ale może powiedzieć, że drugie znaczenie ma zastosowanie, więc==
token użyje tego drugiego, nawet jeśliT
przeciąża operator sprawdzania równości (np. Jeśli jest typuString
) ..Equals()
Pracuje dla mnie natomiastTKey
to typ rodzajowy.źródło
x.Id.Equals
nie takid.Equals
. Prawdopodobnie kompilator wie coś o typiex
.