Thursday, February 21, 2013

When the new keyword met reflection

The New Keyword

Consider the following code:

    public class BaseClass
    {
        public int? TheProperty { get; set; }
    }

    public class DerivedClass : BaseClass
    {
        public new int TheProperty
        {
            get { return base.TheProperty ?? 0; }
            set { base.TheProperty = value; }
        }
    }

The new keyword is used in order to hide a property or method of the base class. It can be used in order to give another implementation for the property even if the property is not defined as virtual in the base class. As it hides the property of the base class it can even have a different type. In this example, the property of the base class is of type int? while the property of the derived class is of type int.
In the following code the property of the base class is called:

            BaseClass instance = new DerivedClass();
            instance.TheProperty = null;

While in the following code the property of the derived class is called:

            DerivedClass instance = new DerivedClass();
            instance.TheProperty = 5;

The Problem

The following method is using reflection in order to get the value of a property:

        public static object GetPropertyValue(object obj, string propertyName)
        {
            System.Reflection.PropertyInfo propertyInfo = obj.GetType().GetProperty(propertyName);
            object value = propertyInfo.GetValue(obj, null);
            return value;
        }

This method generally works fine, but when obj is an instance of DerivedClass (and propertyName is "TheProperty"), the first line of the method will throw the following exception: "Ambiguous match found.". That is because the DerivedClass does not have a single property called TheProperty, but rather two different properties of different types by that name.

Possible Solutions

First we should consider dropping the new keyword. Hiding a property is not an obvious thing to do, so first I would review why hiding was used in the first place. As we seen in this post hiding can be a little confusing so I think it should be considered carefully.

If we do decide to keep hiding the property, we can modify the GetPropertyValue method to support it by using the following code:
        
        public static object GetPropertyValue(object obj, string propertyName)
        {
            System.Reflection.PropertyInfo propertyInfo = 
                obj.GetType().GetProperties().FirstOrDefault(prop => prop.Name == propertyName);
            object value = propertyInfo.GetValue(obj, null);
            return value;
        }

If you are looking specifically for any one of the properties you can update the predicate used by FirstOrDefault to consider DeclaringType (for example, prop.DeclaringType == typeof(DerivedClass)) or PropertyType (for example, prop.PropertyType == typeof(int?)).

In our case we decided to modify the GetPropertyValue as it is part of our infrastructure, and we decided we would rather have our infrastructure support such cases.

No comments:

Post a Comment