Reflection API Changes in .NET 4.5 Applications and Fun with Custom Reflection Context

By Anoop Madhusudanan

Vote on HN

imageBefore .NET 4.5, as you are aware, the Type class in System namespace was used for most of the reflection scenarios. Though you can use Type class to inspect meta data, this is really heavy weight. As most reflection scenarios are just read only (for example, using attributes for providing meta data etc),  Microsoft decided to introduce a light weight, alternate Type API as part of .NET Core profile. If you are wondering about different .NET profiles, here is a good list compiled together.

Type and TypeInfo

So to keep the long story short - in .NET 4.5 onwards, Type class got ‘shrunk’ to provide a shallow, high speed read only view of an object’s structure (definition). And TypeInfo class got introduced that contains the actual type definitions with more detailed information. See Type and TypeInfo – and check out the platform specific support for it’s members. You can read more about the Reflection API changes here

The TypeInfo class represents type definitions and the Type class represents type references. Given a Type object, you can get the name of the type as a string, without any requirement to load anything more. Alternatively, if you need rich information about a type, you can get a TypeInfo object from a Type object. 

In .NET full profiles used for desktop and web applications, the old Type API (Accessing everything through Type) is still available to maintain backward compatibility, along with the new Type API (TypeInfo etc). And For .NET Framework profiles for new platforms like WinRT that don’t need backward compatibility, Type class got shrunk by removing a number of members as discussed, and TypeInfo is the only way to dive deep into reflection. If you are writing code that should target multiple framework versions (as in Portable Class Libraries), you should use the new API. Otherwise, you can continue to use the old API as .NET framework versions on full profile actually returns the self contained Type object as previously.

Using TypeInfo

You may call GetTypeInfo on any type object as shown below to get the TypeInfo for a Type, and may use the new API (like DeclaredProperties) to traverse the members.

	//Get the basic type
        Type myType = "somestring".GetType();

        //Get more information about that type
        TypeInfo myTypeDetails = myType.GetTypeInfo();

        foreach(var prop in myTypeDetails.DeclaredProperties)
            {
                Console.WriteLine(prop.Name);
            }

Also, you may use TypeInfo to check the relations between types. For example, here is how to use IsAssignableFrom method of TypeInfo to determine variance.

  //Can I assign a string to an object - True
   var canAssignStringToObject = typeof (object).GetTypeInfo()
                                  .IsAssignableFrom(typeof (string));

  //Can I assign an object to a string - False
  var canAssignObjectToString = typeof(string).GetTypeInfo()
                                 .IsAssignableFrom(typeof(object));

Custom Reflection Context

Custom Reflection Context is a new addition in .NET 4.5, to customize how a consumer using the TypeInfo based new Reflection API views reflected information about your types. This will be extremely helpful when you write Libraries and design components that take advantage of meta data. This is much like customizing Type information using techniques like type descriptors. The new API provides run time virtualization over reflection, and looks quite intuitive - I wish this was there when we spend years writing custom tools for extending Visual Studio few years back, to virtualize type information for existing and new components.

You can override properties in CustomReflectionContext to add virtualized properties, custom attributes etc. For example, here is a quick PropertyAdder context that’ll inject some properties while accessed through the custom reflection context

.
    //Just a dummy Dog class
    //See that we don't have any properties
    class Dog
    {

    }

//A PropertyAdder context
    //To add custom properties to the reflection context
    class PropertyAdder : CustomReflectionContext
    {
        private readonly Dictionary _properties;

        public PropertyAdder(Dictionary properties)
        {
            _properties = properties;
        }

        protected override IEnumerable AddProperties(Type type)
        {
            if (type == typeof(Dog))
            {
                foreach (var p in _properties)
                {
                    yield return
                        CreateProperty(MapType(p.Value.GetType().GetTypeInfo()),
                                       p.Key,
                                       (o) => _properties[p.Key],
                                       (o, v) => _properties[p.Key] = v);
                }
            }
            else
            {
                base.AddProperties(type);
            }
        }
    }

And here is a quick example for using our PropertyAdder. Essentially,

  • We are creating a new instance of PropertyAdder
  • Then decorating our Dog class’s type information by mapping the type information via the custom reflection context.
  • Finally, we are creating an instance of Dog class, and look ma- we’ve got the properties via reflection

So, here we go.

    //Main Driver
    class Program
    {
        static void Main(string[] args)
        {

            //Properties we want to add
            var properties = new Dictionary
                {
                    {"Name", "Joe"},
                    {"Age", 10}
                };

            //An instance of our custom reflection context
            var adderContext = new PropertyAdder(properties);

            //Get the type in the default reflection context.
            var dogTypeInfo = typeof (Dog).GetTypeInfo();

            //Get the type in the customized reflection context.
            //We'll map the context with our PropertyAdder
            var customDogTypeInfo = adderContext.MapType(dogTypeInfo);


            //Just an instance
            var d = new Dog();

            //Display properties and values using custom reflection context. 
            foreach (var prop in customDogTypeInfo.DeclaredProperties)
            {

                //We'll really see the properties we added above
                Console.WriteLine("{0}={1}",prop.Name, 
                    customDogTypeInfo.GetProperty(prop.Name).GetValue(d));
            }

            Console.ReadLine();
        }
    }

And here is the output. You’ll see that we are accessing the properties we added via the reflection context. Here you go.

image

So this is fun especially when you work with libraries that leverage meta data and on the tooling side. For example, the RegistrationBuilder class in MEF 2.0 inherits custom reflection context to provide meta data based conventions that describes rules for decorating entities.

Happy Coding Smile. Also, see my other C# posts here

© 2012. All Rights Reserved. Amazedsaint.com