2013-01-31 25 views
8

Rozważmy T = string.W jaki sposób EqualityComparer <T> .Default działa wewnętrznie?

Jestem ciekawy, czy używa coś takiego: typeof(EqualityComparer<T>).GetInterface("IEqualityComparer<T>");

wszelkie sugestie ..

+0

Po co miałoby mówić 'GetInterface', aby uzyskać obiekt' System.Type' dla interfejsu, który sam implementuje? Jak byłoby to przydatne? Mógłby po prostu powiedzieć 'typeof (IEqualityComparer )' jeśli potrzebował tego typu, ale nie potrzebuje tego. Musi zwrócić instancję 'EqualityComparer ', której możemy użyć. Ponieważ klasa jest "abstrakcyjna", wiąże się to z tworzeniem instancji niektórych nie abstrakcyjnych klas pochodnych i zwracaniem tego. Ale może naprawdę pytasz, jak zachowuje się zwracany obiekt? (Przynajmniej tak odpowiedziałem poniżej.) –

Odpowiedz

9

Dzięki uprzejmości Reflektor:

public static EqualityComparer<T> Default 
{ 
    get 
    { 
     EqualityComparer<T> defaultComparer = EqualityComparer<T>.defaultComparer; 
     if (defaultComparer == null) 
     { 
      defaultComparer = EqualityComparer<T>.CreateComparer(); 
      EqualityComparer<T>.defaultComparer = defaultComparer; 
     } 
     return defaultComparer; 
    } 
} 

private static EqualityComparer<T> CreateComparer() 
{ 
    RuntimeType c = (RuntimeType) typeof(T); 
    if (c == typeof(byte)) 
    { 
     return (EqualityComparer<T>) new ByteEqualityComparer(); 
    } 
    if (typeof(IEquatable<T>).IsAssignableFrom(c)) 
    { 
     return (EqualityComparer<T>) RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType) typeof(GenericEqualityComparer<int>), c); 
    } 
    if (c.IsGenericType && (c.GetGenericTypeDefinition() == typeof(Nullable<>))) 
    { 
     RuntimeType type2 = (RuntimeType) c.GetGenericArguments()[0]; 
     if (typeof(IEquatable<>).MakeGenericType(new Type[] { type2 }).IsAssignableFrom(type2)) 
     { 
      return (EqualityComparer<T>) RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType) typeof(NullableEqualityComparer<int>), type2); 
     } 
    } 
    if (c.IsEnum && (Enum.GetUnderlyingType(c) == typeof(int))) 
    { 
     return (EqualityComparer<T>) RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType) typeof(EnumEqualityComparer<int>), c); 
    } 
    return new ObjectEqualityComparer<T>(); 
} 

Więc jak widać, jeśli T = łańcuch powróci GenericEqualityComparer<string>.

+0

Zadzwoń do mnie: P –

+2

Nie wiem, czy to jest pomocne. Pokazuje, z której klasy niepopularnej korzysta się za sceną, ale nie zawiera informacji o tym, jak działa niepubliczna klasa "GenericEqualityComparer ". Więc kogo obchodzi, co konkretna realizacja klasy abstrakcyjnej nazywa się? –

2

EqualityComparer<T>.Default prace poprzez wywołanie metody virtualEquals(object) i GetHashCode() które są zdefiniowane przez System.Object ale może lub nie może być przesłonięta przez T.

Należy pamiętać, że ze względu na metody są virtual, można zastosować implementację bardziej pochodnej klasy niż T. Na przykład:

EqualityComparer<object>.Default 
    .Equals(new Uri("http://example.com/"), new Uri("http://example.com/")) 

powróci true, nawet jeśli

Object.ReferenceEquals(new Uri("http://example.com/"), new Uri("http://example.com/")) 

i

(object)new Uri("http://example.com/") == (object)new Uri("http://example.com/") 

zarówno powrócić false.

W przypadku, gdy T jest string, klasa System.String przeciąża dwie metody i stosuje porównanie porządkowe. Tak więc w tym przypadku powinien być równoważny z System.StringComparer.Ordinal. I oczywiście string jest klasą sealed, więc żadna inna klasa nie mogłaby pochodzić z string i zastąpić Equals i GetHashCode w dziwny sposób.

+1

Również - Zaleta polega na tym, że najpierw sprawdza, czy T implementuje 'IEquatable ', a jeśli tak, wywołuje tę implementację zamiast tego unikając boksowania. Jest to szczególnie przydatne w przypadku metod ogólnych: 'static bool Foo (T x, T y) { bool same = EqualityComparer .Default.Equals (x, y);' –

Powiązane problemy