The identity most users would expect MemberInfos (other than Type) to have is not what Reflection provides. So for example most folks would expect the follow program to print true instead of false:

 

    public class B { public void M() { } }

    public class D : B { }

 

    public class Program

    {

        public static void Main()

        {

            Console.WriteLine(typeof(B).GetMethod("M") == typeof(D).GetMethod("M"));

        }

    }

 

The MethodInfos are not equal because ReflectedType is included in the idenity of MethodInfos. The identity Reflection uses for MethdInfo can be expressed as:

 

DeclaringType + MethodName + Signature + DeclaringType instantiation + Method Instantiation + ReflectedType

 

And what most users would expect it to be can be expressed as:

 

DeclaringType + MethodName + Signature + DeclaringType Instantiation + Method Instantiation

 

We can’t make them match because that would be a breaking change. However here is a little code snippet that will do the comparison using the latter definition of identity. This will work for all subclasses of MemberInfo (Type, MethodBase, MethodInfo, ConstructorInfo, FieldInfo, PropertyInfo and EventInfo):

  

    public bool MemberInfoEquals(MemberInfo lhs, MemberInfo rhs)

    {

        if (lhs == rhs)

            return true;

 

        if (lhs.DeclaringType != rhs.DeclaringType)

            return false;

 

        // Methods on arrays do not have metadata tokens but their ReflectedType

        // always equals their DeclaringType

        if (lhs.DeclaringType != null && lhs.DeclaringType.IsArray)

            return false;

 

        if (lhs.MetadataToken != rhs.MetadataToken || lhs.Module != rhs.Module)

            return false;

 

        if (lhs is MethodInfo)

        {

            MethodInfo lhsMethod = lhs as MethodInfo;

 

            if (lhsMethod.IsGenericMethod)

            {

                MethodInfo rhsMethod = rhs as MethodInfo;

 

                Type[] lhsGenArgs = lhsMethod.GetGenericArguments();

                Type[] rhsGenArgs = rhsMethod.GetGenericArguments();

                for (int i = 0; i < rhsGenArgs.Length; i++)

                {

                    if (lhsGenArgs[i] != rhsGenArgs[i])

                        return false;

                }

            }

        }

        return true;

    }

 

The Get and Set Methods on arrays (typeof(int[]).GetMethods()) do not have metadata tokens. Instead they are cooked up by the loader at runtime.

 

We replaced MethodName + Signature with Module + MetadataToken. We could have also used the handles (MethodHandle and FieldHandle) but seeing as those are not defined on MemberInfo that would have complicated the code a bit.

 

Every System.Type's DeclaringType always equals it's ReflectedType.

 

One final note on the identity of MethodHandles. The runtime has an optimization that sometimes shares MethodHandles between different generic instantiations. That is why MethodHandles can replace MethodName + Signature but do not include DeclaringType Instantiation or Method Instantiation.