Weitao Su's WebLog

.Net Reflection and more

  • Override properties (III): Type.GetProperties

    I have discussed some aspects of property inheritance in my previous posts.

    To recap, properties are intrinsically different from methods in that they are simply metadata artifact. You never see them in IL and more importantly to our discussion, they DON'T override each other.

    In this third installment I will talk about Type.GetProperties.

    Those who are familar with Type.GetMethods already know that if a method on a parent type is overriden in a sub type, GetMethods on the subtype will only return the overriding method, not the overriden one. We also want to mantain this semantics in GetProperties. To do that we need to be able to match a overriding property with the overriden one. In the past we did this by comparing property names and signatures. This worked relatively well untill we introduced generics in V2.

        public class Base<T>

        {

            public virtual T MyProperty { get { return default(T); } }

        }

        public class Derived : Base<int>

        {

            public override int MyProperty { get { return 42; } }

        } 

    In the sample code above, the signature of the two MyProperty are T and int respectively and don't match. As a result we don't treat Derived<int>.MyProperty as an override of Base<T>.MyProperty and return both of them in GetProperties.

    A better maching criteria is the getter/setter methods. In the same code sample, we can easily determine that Derived<int>.MyProperty does override Base<T>.MyProperty becasue the getter of the latter overrides the getter of the former. This is what we are using in Visual Studio 2010 and .Net Framework 4.0. You can download the beta here: http://www.microsoft.com/visualstudio/en-us/products/2010/default.mspx

    Note that Attribute.GetCustomAttributes also uses the same criteria (getters and setters) to find inherited properties.

  • Bing

    http://www.bing.com

    Now can anyone tell me where that frontpage picture is taken?

  • Override properties (II): GetCustomAttributes

    In my last post (http://blogs.msdn.com/weitao/archive/2009/05/28/override-properties-i.aspx) I revealed the truth behind property overriding. In today's article I will talk about how that impact the behavior of GetCustomAttributes.

    In reflection a property is represented by a PropertyInfo object. There are two ways to get the custom attributes off of a PropertyInfo object:

    1. On System.Reflection.MemberInfo (which is the base class of PropertyInfo):
      • public abstract Object[] GetCustomAttributes(bool inherit)
      • public abstract bool IsDefined(Type attributeType, bool inherit)
    2. On System.Attribute:
      • public static Attribute[] GetCustomAttributes(MemberInfo member, bool inherit)
      • public static bool IsDefined(MemberInfo element,Type attributeType, bool inherit)

    The inherit boolean value, according to MSDN, "Specifies whether to search this member's inheritance chain to find the attributes". However, from the previous post we've already learned that there is no real inheritance chain for properties. The property "inheritance" is actually defined by the inheritance of the getter/setter methods.

    The two GetCustomAttributes APIs are actually designed for different purposes.

    1. MemberInfo.GetCustomAttributes strictly reflects the definitions in the metadata.
      • It ignores the "inherit" boolean value for PropertyInfo, EventInfo, and ParameterInfo and doesn't search the inheritance chain at all.
      • It returns all custom attribute instances, including the non-"CLS compliant" ones that don't inherit from System.Attribute.
    2. Attribute.GetCustomAttributes, on the other hand, is closer to the definition in the high level managed languages like C#.
      • It honors the "inherit" boolean value for PropertyInfo, EventInfo, and ParameterInfo. If "inherit" is true, it will search the "inheritance" chain defined by their associated methods.
        • For properties, these are their getters and setters.
        • For events, these are their adders and removers.
        • For parameters, these are their declaring methods.
      • According to CLS Rule 41: "Attributes shall be of type System.Attribute, or a type inheriting from it". Attribute.GetCustomAttributes only returns the custom attributes whose types inherit from System.Attribute.

    So don't be surprised to find the two APIs return different results in the following code sample:

        [AttributeUsage(AttributeTargets.Property, Inherited = true)]

        public sealed class MyAttribute : Attribute { }

        public class Base

        {

            [MyAttribute]

            public virtual int MyProperty { get { return 0; } }

        }

        public class Derived : Base

        {

            public override int MyProperty { get { return 42; } }

        }

        class Program

        {

            static void Main()

            {

                PropertyInfo p = typeof(Derived).GetProperty("MyProperty");

                Console.WriteLine(p.IsDefined(typeof(MyAttribute), true));

                Console.WriteLine(Attribute.IsDefined(p, typeof(MyAttribute), true));

            }

        }

  • Override properties (I)

    I'll start today's discussion with a question: do virtual properties exist and can you override them?

        public class Base

        {

            public virtual int MyProperty { get { return 0; } }

        }

     

        public class Derived : Base

        {

            public override int MyProperty { get { return 42; } }

        }

    From this code sample it seems pretty obvious that the answer is yes. But let's check the IL generated from this code sample:

    .property instance int32 MyProperty()

    {

    .get instance int32 Program.Base::get_MyProperty()

    } // end of property Base::MyProperty

     

    .property instance int32 MyProperty()

    {

    .get instance int32 Program.Derived::get_MyProperty()

    } // end of property Derived::MyProperty

    Now where does it say virtual and override?

    It turns out, unlike methods, properties cannot really "override" other properties. The property overriding we saw in the previous code sample is actually an syntatic artifact added by the C# compiler (and many others). Here is a excerpt from the CLI spec (ECMA 335), Partition I:

    8.10.3 Property and event inheritance

    Fundamentally, properties and events are constructs of the metadata intended for use by tools that target the

    CLI and are not directly supported by the VES itself. Therefore, it is the job of the source language compiler

    and the reflection library (see Partition IV) to determine rules for name hiding, inheritance, and so forth. The

    source compiler shall generate CIL that directly accesses the methods named by the events and properties, not

    the events or properties themselves.

    Again using the previous C# code sample as an example, the real override is defined on the property getters, not the properties themselves:

    .method public hidebysig newslot specialname virtual

    instance int32 get_MyProperty() cil managed

    { ... ... } // end of method Base::get_MyProperty

     

    .method public hidebysig specialname virtual

    instance int32 get_MyProperty() cil managed

    { ... ... } // end of method Derived::get_MyProperty

    The fact that properties don't really override other properties have subtle impact on many Reflection APIs, some of which often catch users by surprise. I will talk about them in the next installments.

    Note that Property is not the only member that has this fake inheritance. Event and sometimes Parameter have similar semantics.

  • Formal type parameters of generic types (2).

    Last time I blogged about the formal type parameters of generic types in reflection (http://blogs.msdn.com/weitao/archive/2008/03/19/formal-type-parameters-of-generic-types.aspx). Here I will present an example in Reflection.Emit.

    Reflection doesn't distinguish between a generic type/method definition and a generic type/method instantiated on its formal type parameters. So a type that represents the former can also mean the latter. In most cases this is not a problem. But in Reflection.Emit, sometimes we need to know which one it is. When a user calls ILGenerator.Emit(Opcode, Type/MethodInfo) and the Type/MethodInfo passed in is a generic type/method definition we have to know which one it is to emit the correct code because they are represented by completely different tokens in the metadata. Fortunately depending on the context, it can only mean one at any time:

    1. If we are emitting the code to call a method, this can only be a generic method instantiation, never a generic definition.
    2. If we are emitting a ldtoken instructions, we should get a method definition, not an instantiation.

    I will show some sample code in the next post.

  • Formal type parameters of generic types.

    I recently got a question on generic type identity. I will illustrate the question using this simple app:

     

    class Base<T> { }

    class Derived<T> : Base<T> { }

    class App

    {

        static void Main()

        {

            Console.WriteLine(typeof(Base<>) == typeof(Derived<>).BaseType);

        }

    }

     

    Can you guess what will be printed out?

    Since the base type of Derived<T> is Base<T>, some might think this sample will print out true. However false was printed out. Why?

     

    To answer this question we must first understand the difference between the formal type parameter and the actual type parameter. Formal type parameters are those that appear in definitions of generic types. They are templates that will be replaced when a constructed generic type is instantiated from the generic type definition. The types that replace them are the actual type parameters. Sometimes actual type parameters of one type can be the formal type parameters of another. In class Base<T>, T is the formal type parameter of type Base. In class Derived<T> : Base<T>, T is the formal parameter of Derived. It is also used to instantiated Base so it is the actual parameter for Base. The key here is that the T in Base<T> is different from the T in Derived<T>. They share the same name but are actully two different types.

     

    Are you confused? It will be easier to understand if I had used different names for the formal parameters of Base and Derived:

     

    class Base<T> { }

    class Derived<U> : Base<U> { }

    class App

    {

        static void Main()

        {

            Console.WriteLine(typeof(Base<>) == typeof(Derived<>).BaseType);

        }

    }

     

    The formal type parameter of Base is still called T. But the formal type parameter of Derived is called U now which is also used to instantiated Base. So the base type of Derived<U> is Base<T> with the formal type parameter T replaced by an actual type U. No wonder it is not the same type as Base<T>.

     

    If you ILDASM the first sample above and enable “show token values” you will see:

     

    .class /*02000002*/ private auto ansi beforefieldinit Base`1<T>

           extends [mscorlib/*23000001*/]System.Object/*01000001*/

    {

    } // end of class Base`1

     

    .class /*02000003*/ private auto ansi beforefieldinit Derived`1<T>

           extends class Base`1/*02000002*/<!T>/*1B000001*/

    {

    } // end of class Derived`1

     

    The token for Base`1<T> is 02000002 which is a typedef. The token for the base type of Derived`1<U> is 1B000001 which is a typespec. We all know that typespec tokens represent arrays, byrefs, pointers, and instantiated generic types. In this case it is an instantiated generic type which is different from its generic type definition represented by the typedef token 02000002. In MetaInfo you will see:

     

    TypeDef #1 (02000002)

    -------------------------------------------------------

          TypDefName: Base`1  (02000002)

          Flags     : [NotPublic] [AutoLayout] [Class] [AnsiClass] [BeforeFieldInit]  (00100000)

          Extends   : 01000001 [TypeRef] System.Object

          1 Generic Parameters

                (0) GenericParamToken : (2a000001) Name : T flags: 00000000 Owner: 02000002

          Method #1 (06000001)

          -------------------------------------------------------

                MethodName: .ctor (06000001)

               Flags     : [Public] [HideBySig] [ReuseSlot] [SpecialName] [RTSpecialName] [.ctor]  (00001886)

                RVA       : 0x00002050

                ImplFlags : [IL] [Managed]  (00000000)

                CallCnvntn: [DEFAULT]

                hasThis

                ReturnType: Void

                No arguments.

     

    ... ... ... ...

     

    TypeSpec #1 (1b000001)

    -------------------------------------------------------

          TypeSpec : GenericInst Class Base`1< Var!0>

          MemberRef #1 (0a000005)

          -------------------------------------------------------

                Member: (0a000005) .ctor:

                CallCnvntn: [DEFAULT]

                hasThis

                ReturnType: Void

                No arguments.

     

     

    Now can you guess the output of the following program?

     

    class Base<T> { }

    class Derived<U> : Base<U> { }

    class App

    {

        static void Main()

        {

            Type t1 = typeof(Base<>);

            Type t2 = typeof(Derived<>);

            Type t3 = t2.BaseType;

     

            Console.WriteLine(t1.IsGenericTypeDefinition);

            Console.WriteLine(t2.IsGenericTypeDefinition);

            Console.WriteLine(t3.IsGenericTypeDefinition);

     

            Type p1 = t1.GetGenericArguments()[0];

            Type p2 = t2.GetGenericArguments()[0];

            Type p3 = t3.GetGenericArguments()[0];

     

            Console.WriteLine(p1 == p2);

            Console.WriteLine(p1 == p3);

            Console.WriteLine(p2 == p3);

     

            Type t4 = t1.MakeGenericType(p1);

            Type t5 = t1.MakeGenericType(p2);

            Type t6 = t1.MakeGenericType(p3);

     

            Console.WriteLine(t4 == t1);

            Console.WriteLine(t5 == t1);

            Console.WriteLine(t6 == t1);

            Console.WriteLine(t4 == t3);

            Console.WriteLine(t5 == t3);

            Console.WriteLine(t6 == t3);

     

        }

    }

     

  • Binding to hidden properties using Reflection

    Thottam blogged about how C# and Reflection differ in binding to hidden properties: http://blogs.msdn.com/thottams/archive/2006/03/17/553376.aspx. So for the hidden properties you would not be able to bind to statically in C#, you can bind to them using Reflection. Consider the following class hierarchy:

    class A

    {

          public string P

          {... ...}

    }

     

    class B : A

    {

          public new int P

          {... ...}

    }

     

    class C : B

    {

          public new object P

          {... ...}

    }

    C.P hides B.P which in turn hides A.P. From the type C. If you want to get all the properties of C (including declared and inherited), you can do this:

            Properties[] ps2 = typeof(C).GetProperties();

    If you want to get all the properties declared on C, you can set the binding flag to DeclaredOnly.

            Properties[] ps1 = typeof(C).GetProperties(BindingFlags.DeclaredOnly);

    And with some set arithmetics it is not too difficult to get all the properties C inherited from B and A.

    I used GetProperties in my illlustration above. GetProperty is similar except that if there are multiple hits, you get a System.Reflection.AmbiguousMatchException.

    If you just want to get B.P from C, you can use one of the overrides of Type.GetProperty that allows you to specify the return type:

            typeof(C).GetProperty("P", typeof(int));

    If a property on the subclass has the exact same name and signature as the one in the superclass, you can call GetProperties() to get all the properties first and then use the DeclaredType property on PropertyInfo to find out the one you want.

    The binding semantics for fields and methods are also very similar.

    Note that in the sample above C.P HIDES B.P and A.P. And they are all considered different properties. If a property in a subclass OVERRIDES a property in the superclass, they are considered the same property. So you will get only one property when you call GetProperties on the subclass.


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