This api was approved here:
https://github.com/dotnet/corefx/issues/5884
and is a necessary step to fixing the System.Dynamic.Runtime.Tests
failure:
https://github.com/dotnet/corefx/issues/19895
which is caused by Microsoft.CSharp trying to do the impossible
and emulate this api without GetMetadataToken() support.
This approach opts for the most straightforward and efficient
implementation without any special-casing for weird situations
(this is also what Microsoft.CSharp implements today as
well as what someone else trying to trampoline members
across generic instantaitions is like to do.)
This results in the following behavior for these
corner cases. With the possible exception of #3,
I think they are tolerable enough to accept and codify:
1. "other" implemented by an entirely different Reflection
provider than "this".
Behavior:
returns false without invoking any methods on the
"other" Member.
To change it to throw an ArgumentException would
mean extra cast checks against the 6 possible
Runtime types (or having said RuntimeTypes implement
a sentinel interface.)
Given that HasSameMetadataDefinitionAs() is a
"looser cousin of Equals()" and "Equals()"
doesn't throw for objects from a different universe,
this seems reasonable.
2. Arrays, ByRefs, Pointers and Types from GetTypeFromCLSID()
Behavior:
Arrays, ByRefs, Pointers all return token 0x0600000
and so they'll return "true" wrt to each other (provided
both types are implemented by the same provider.)
CLSID types all return the typedef of __ComObject
so they'll return "true" wrt to each other.
The constructor exposed by CLSID types all return
the typedef of some constructor on __ComObject so
they'll return "true" wrt to each other.
I do not think these are interesting cases that merit
special handling. These types will never appear
in an enumeration of the members of a type. (The
fact that Reflection surfaces them in objects
that are assignable to MemberInfo is a structural
flaw in Reflection's object model.)
3. Synthesized constructors and methods on array types.
Behavior:
These methods all return 0x06000000 as a token
so the constructors will all compare true wrt
each other, and likewise with the methods.
This is a bit crummy though it's not clear
what the "right" policy should look like.
I could be persuaded to throw NotSupported
for these, to leave the possibility open
for a better story later. On the other hand,
I wouldn't demand it either.
}
}
+ public virtual bool HasSameMetadataDefinitionAs(MemberInfo other) { throw NotImplemented.ByDesign; }
+
public abstract bool IsDefined(Type attributeType, bool inherit);
public abstract object[] GetCustomAttributes(bool inherit);
public abstract object[] GetCustomAttributes(Type attributeType, bool inherit);
public abstract partial class MemberInfo
{
internal virtual bool CacheEquals(object o) { throw new NotImplementedException(); }
+
+ internal bool HasSameMetadataDefinitionAsCore<TOther>(MemberInfo other) where TOther : MemberInfo
+ {
+ if (other == null)
+ throw new ArgumentNullException(nameof(other));
+
+ // Ensure that "other" is a runtime-implemented MemberInfo. Do this check before calling any methods on it!
+ if (!(other is TOther))
+ return false;
+
+ if (MetadataToken != other.MetadataToken)
+ return false;
+
+ if (!(Module.Equals(other.Module)))
+ return false;
+
+ return true;
+ }
}
}
}
}
+ public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore<RuntimeConstructorInfo>(other);
+
public override Type ReflectedType
{
get
}
}
public override Type DeclaringType { get { return m_declaringType; } }
+ public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore<RuntimeEventInfo>(other);
public override Type ReflectedType
{
get
}
}
+ public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore<RuntimeFieldInfo>(other);
+
public override Module Module { get { return GetRuntimeModule(); } }
#endregion
}
}
+ public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore<RuntimeMethodInfo>(other);
+
public override Type ReflectedType
{
get
}
}
+ public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore<RuntimePropertyInfo>(other);
+
public override Type ReflectedType
{
get
#endregion
#region Misc
+ public sealed override bool HasSameMetadataDefinitionAs(MemberInfo other) => HasSameMetadataDefinitionAsCore<RuntimeType>(other);
+
public override bool IsTypeDefinition
{
get { return RuntimeTypeHandle.IsTypeDefinition(this); }