気になりません?
純粋にどうやって生成してるのか疑問に思ったので気が済むまで追ってみる。 完全に備忘録でありいろいろ雑。
using System.Runtime.CompilerServices; namespace System.Collections.Generic { [TypeForwardedFrom("mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089")] [TypeDependency("System.Collections.Generic.ObjectEqualityComparer`1")] [Serializable] public abstract class EqualityComparer<T> : IEqualityComparer, IEqualityComparer<T> { public static EqualityComparer<T> Default { [Intrinsic] get; } = (EqualityComparer<T> ) ComparerHelpers.CreateDefaultEqualityComparer(typeof (T)); public abstract bool Equals(T x, T y); public abstract int GetHashCode(T obj); internal virtual int IndexOf(T[] array, T value, int startIndex, int count) { int num = startIndex + count; for (int index = startIndex; index < num; ++index) { if (this.Equals(array[index], value)) return index; } return -1; } internal virtual int LastIndexOf(T[] array, T value, int startIndex, int count) { int num = startIndex - count + 1; for (int index = startIndex; index > = num; --index) { if (this.Equals(array[index], value)) return index; } return -1; } int IEqualityComparer.GetHashCode(object obj) { if (obj == null) return 0; if (obj is T) return this.GetHashCode((T) obj); ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison); return 0; } bool IEqualityComparer.Equals(object x, object y) { if (x == y) return true; if (x == null || y == null) return false; if (x is T && y is T) return this.Equals((T) x, (T) y); ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_InvalidArgumentForComparison); return false; } } }
16行目が生成しているところ。関数はこんな感じ。
internal static object CreateDefaultEqualityComparer(Type type) { object obj = (object) null; RuntimeType runtimeType = (RuntimeType) type; if (type == typeof (byte)) obj = (object) new ByteEqualityComparer(); else if (typeof (IEquatable<> ).MakeGenericType(type).IsAssignableFrom(type)) obj = RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType) typeof (GenericEqualityComparer<int> ), runtimeType); else if (type.IsGenericType) { if (type.GetGenericTypeDefinition() == typeof (Nullable<> )) obj = ComparerHelpers.TryCreateNullableEqualityComparer(runtimeType); } else if (type.IsEnum) obj = ComparerHelpers.TryCreateEnumEqualityComparer(runtimeType); return obj ?? RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType) typeof (ObjectEqualityComparer<object> ), runtimeType); }
いろいろ知らない。 Typeはこう。
public abstract class Type : MemberInfo, IReflect
でRuntimeTypeはこう。internalらしい。
internal class RuntimeType : TypeInfo, ICloneable
でこのTypeInfoが
public abstract class TypeInfo : Type, IReflectableType
なるほどRuntimeTypeはランタイムの中だけで使われる具象っぽい。
一番始めにbyte型か確認してる。これはいい。 次に
(typeof (IEquatable<> ).MakeGenericType(type).IsAssignableFrom(type))
これはなにか。
(typeof (IEquatable<> ).MakeGenericType(type)
でIEquatable
public struct Int32 : IComparable, IConvertible, IFormattable, IComparable<int> , IEquatable<int> , ISpanFormattable
IEquatable
ByteEqualityComparer(); RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType) typeof (GenericEqualityComparer<int> ), runtimeType); RuntimeTypeHandle.CreateInstanceForAnotherGenericParameter((RuntimeType) typeof (ObjectEqualityComparer<object> ), runtimeType); ComparerHelpers.TryCreateNullableEqualityComparer(runtimeType); ComparerHelpers.TryCreateEnumEqualityComparer(runtimeType);
の4つであることがわかる。byteとNullableは特別扱いなんですねー。 最終的にobjがnullだった場合、typeof (ObjectEqualityComparer<object>)が渡されるのは分かる(Object.Equalsとかが使われるからだろう)のだが、インターフェースが実装されていた場合、typeof (GenericEqualityComparer<int>)が渡されているのは一体どういうことだろう。なぜint。みてみると
[MethodImpl(MethodImplOptions.InternalCall)] internal static extern object CreateInstanceForAnotherGenericParameter( RuntimeType type, RuntimeType genericParameter);
\\internal static extern//
C#で書かれておらず、C++で実装されているらしいですねこのやろう...。 しかし.NET Coreはオープンソースです。ならばソースを見てやろうじゃないかとなるわけです。clrにかかれていると思われるので、ここでCreateInstanceForAnotherGenericParameterで検索します。 するとこれに引っかかります。 引用すると
FCFuncElement("CreateInstanceForAnotherGenericParameter", RuntimeTypeHandle::CreateInstanceForGenericType)
まぁおそらく第一引数の文字列が呼び出し名で、第二引数が呼び出される実際のものなのだろう。ということで次はCreateInstanceForGenericTypeをリポジトリ内で検索する。そうするとこれに引っかかる。引用しつつコメント書きながら読んでみます。
FCIMPL2(Object*, RuntimeTypeHandle::CreateInstanceForGenericType, ReflectClassBaseObject* pTypeUNSAFE, ReflectClassBaseObject* pParameterTypeUNSAFE) { FCALL_CONTRACT; struct _gc { OBJECTREF rv; REFLECTCLASSBASEREF refType; REFLECTCLASSBASEREF refParameterType; } gc; gc.rv = NULL; //return value? gc.refType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pTypeUNSAFE);//(RuntimeType) typeof (GenericEqualityComparer<int> ) gc.refParameterType = (REFLECTCLASSBASEREF)ObjectToOBJECTREF(pParameterTypeUNSAFE);//runtimeType MethodDesc* pMeth; TypeHandle genericType = gc.refType-> GetType();//(RuntimeType) typeof (GenericEqualityComparer<int> ) TypeHandle parameterHandle = gc.refParameterType-> GetType();//runtimeType _ASSERTE (genericType.HasInstantiation());//RuntimeType) typeof (GenericEqualityComparer<int> )側がInstantiationを持っているか(生成できるかとかそういうニュアンスあるのかなぁ) HELPER_METHOD_FRAME_BEGIN_RET_PROTECT(gc);//gcをプロテクトしてるのかな。 // 多分これがGenericEqualityComparer<runtimeType> 型を生成してそう。Canonical(正典の、教会法に基づく、規準的な、標準的な) //objectとintで特に場合分けとかしてなさそうなのでこれの内部でなにかしてそうだけど...うーん。 TypeHandle instantiatedType = ((TypeHandle)genericType.GetCanonicalMethodTable()).Instantiate(Instantiation(¶meterHandle, 1)); // Get the type information associated with refThis MethodTable* pVMT = instantiatedType.GetMethodTable();//virutual method table pointerかな _ASSERTE (pVMT != 0 && !instantiatedType.IsTypeDesc()); _ASSERTE(!(pVMT-> GetAssembly()-> IsDynamic() && !pVMT-> GetAssembly()-> HasRunAccess())); _ASSERTE( !pVMT-> IsAbstract() ||! instantiatedType.ContainsGenericVariables()); _ASSERTE(!pVMT-> IsByRefLike() && pVMT-> HasDefaultConstructor()); pMeth = pVMT-> GetDefaultConstructor(); MethodDescCallSite ctor(pMeth); // We've got the class, lets allocate it and call the constructor // Nullables don't take this path, if they do we need special logic to make an instance _ASSERTE(!Nullable::IsNullableType(instantiatedType));//Nullableだと怒られる(だからかC#側で事前に弾いているね) gc.rv = instantiatedType.GetMethodTable()-> Allocate();//関数テーブルからアロケーションしてる ARG_SLOT arg = ObjToArgSlot(gc.rv); // Call the method TryCallMethod(&ctor, &arg, true); HELPER_METHOD_FRAME_END(); return OBJECTREFToObject(gc.rv); } FCIMPLEND //FCIMPLEND...function implementation endの略かな。
わからんw
関数テーブルからAllocateしていたりInstantiateしてるんですね。よくわからないけど面白い。これ以上に深追いは時間が無限に溶ける気がするので止めておく。。。