kingces's WebLog

  • MemberInfo Identity, Comparing MemberInfos

    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.

     

     

  • Never use Type.ReflectedType

    Never use MemberInfo.ReflectedType. ReflectedType is not well defined and so logic which depends on it is also not well defined. Using ReflectedType won’t crash your system but chances are that you’ve got a bug in whatever logic is using ReflectedType. Such logic should be re-factored to use Type.DeclaringType.

    Reflected type is defined as “the Type from which the MemberInfo was retrieved”. For example, the following code will print out “Base” and “Derived”.

    public class Base { public void Method(); }
    public class Derived : Base { }
    public class Exe
    {
        public static void Main()
        {
            MethodInfo mBase = typeof(Base).GetMethod(“Method”);
            MethodInfo mDerived = typeof(Derived).GetMethod(“Method”);
            Console.WriteLine(mBase.ReflectedType);
            Console.WriteLine(mDerived.ReflectedType);
        }
    }

    However there are ways other than reflecting on a Type (e.g. GetMethod and friends) to get a hold of a MemberInfo (e.g. via a stalk walk or out of an Exception object) and therefore the semantic of ReflectedType is not well defined.

    The root of the problem with the semantic of ReflectedType is that ReflectedType adds an unnecessary dimension to the identity of a MemberInfo. The dimension is unnecessary because MemberInfos that differ only in their ReflectedType (e.g. mBase and mDerived) will otherwise behave exactly the same the same.

    We would remove except that would be a breaking change.

  • Do not derive from System.Type

    Do not derive from System.Type or any other types deriving from System.MemberInfo. You might infer from all those protected members on Type that we intended to have folks derive from Type and have those 3ed party Types somehow "play nice" with the rest of Reflection. This was my first impression however having been the steward of the API for these last few years I would not suggest anyone derive from Type. I'm not sure why we allowed this in the first place. It may have simply been an oversight or it may have been part of some larger vision. But whatever the reason the reality is that most Reflection APIs throw if any Type arguments are not RuntimeTypes or TypeBuilders and so don't offer any support for 3ed party Types (the reason Type is not simply sealed is so that RunitmeType and TypeBuilder to both derive from Type). IMHO we should have made all MemberInfo constructors internal and I suggest you treat the constructors as if they were.

     

  • Hello World!

    I am one of the current stewards of System.Reflection. Feel free to post your reflection scenarios or Reflection related questions if you'd like direct feedback. Thanks, Chris

© 2008 Microsoft Corporation. All rights reserved. Terms of Use  |  Trademarks  |  Privacy Statement
Microsoft
Page view tracker