Reflection Limitation in .NET 3.5/4.0


Recently a colleague of mine pointed to a strange behavior in .NET Reflection  that pushed me doing a lot of additional investigations and finding out a very serious limitation that might raise a lot of issues if not being taken into account.

When you declare property as “virtual” beware that custom attributes won’t be fetched for the derived one if the property is being overrided.

This can be easily tested. First you need creating two custom attributes that will target two cases: virtual property and virtual method.

  [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)]
  public class MyPropertyAttribute : Attribute
  {
  }

  [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
  public class MyMethodAttribute : Attribute
  {
  }

Now you can create two classes: “BaseClass” that will contain a single virtual property and single virtual method and “DerivedClass” that will override both members.

 public class BaseClass
  {
    private string _PropertyVirtual;

    [MyProperty]
    public virtual string PropertyVirtual
    {
      get { return _PropertyVirtual; }
      set { _PropertyVirtual = value; }
    }

    [MyMethod]
    public virtual void MethodVirtual()
    {
    }
  }

  public class DerivedClass : BaseClass
  {
    public override string PropertyVirtual
    {
      get { return base.PropertyVirtual; }
      set { base.PropertyVirtual = value; }
    }

    public override void MethodVirtual()
    {
      base.MethodVirtual();
    }
  }

The problem starts when you try fetching custom attributes for “PropertyVirtual” from within a DerivedClass because you will never get them using PropertyInfo.GetCustomAttributes method call. Here’s a sample Console Application to ensure the limitation is alive:

  class Program
  {
    static void Main(string[] args)
    {
      Console.WriteLine("Checking virtual property attributes:\r\n");

      Console.WriteLine(
        "BaseClass.PropertyVirtual attributes count: {0}",
        typeof(BaseClass).GetProperty("PropertyVirtual").GetCustomAttributes(true).Length);

      Console.WriteLine(
        "DerivedClass.PropertyVirtual attributes count: {0}",
        typeof(DerivedClass).GetProperty("PropertyVirtual").GetCustomAttributes(true).Length);

      Console.WriteLine("\r\nChecking virtual method attributes:\r\n");

      Console.WriteLine(
        "BaseClass.MethodVirtual attributes count: {0}",
        typeof(BaseClass).GetMethod("MethodVirtual").GetCustomAttributes(true).Length);

      Console.WriteLine(
        "DerivedClass.MethodVirtual attributes count: {0}",
        typeof(DerivedClass).GetMethod("MethodVirtual").GetCustomAttributes(true).Length);

      Console.ReadLine();
    }

Upon running this sample you should see the following results:


Checking virtual property attributes:

BaseClass.PropertyVirtual attributes count: 1
DerivedClass.PropertyVirtual attributes count: 0

Checking virtual method attributes:

BaseClass.MethodVirtual attributes count: 1
DerivedClass.MethodVirtual attributes cound: 1

As you can ensure there are no custom attributes returned for overrided virtual property while the expected result is fetched for overrided virtual method.

You can easily find what is happening under the hood if switching to MSIL representation of the classes coded above. Here’s how the “BaseClass” attribute usage looks like:

Reflection Issue 1

As you can see on the IL level “MethodVirtual” declares “MyMethodAttribute” instance right within its own body. The same goes for “PropertyVirtual” – a getter, a setter and attribute declaration.

However both members look like the following when derived:

Reflection Issue 2

While “MethodVirtual” still refers to “base.MethodVirtual” and theoretically can invoke attribute declaration,

note that the following overriding will still work for methods:

  public class DerivedClass : BaseClass
  {
    public override string PropertyVirtual
    {
      get { return base.PropertyVirtual; }
      set { base.PropertyVirtual = value; }
    }

    public override void MethodVirtual()
    {
      //base.MethodVirtual();
    }
  }

“PropertyVirtual” is no longer related to a base implementation. We have a completely new property definition that is wired with base getter and setter. But no custom attribute declaration!

This means that the default code written for MemberInfo class won’t be able fetching anything except empty object array.

There are two workarounds I found that help switching to another MS developer(s) work, custom attribute fetching seems to be completely different in the following approaches: TypeDescriptor and old-fashioned Attribute class.

      Console.WriteLine(
        "DerivedClass.PropertyVirtual attributes count: {0}",
        Attribute.GetCustomAttributes(typeof(DerivedClass).GetProperty("PropertyVirtual"), typeof(MyPropertyAttribute)).Length);

The code above will be able to fetch and display custom attributes for a virtual property. Same can be achieved by means of property descriptors:

      Console.WriteLine(
        "DerivedClass.PropertyVirtual attributes count: {0}",
        TypeDescriptor.GetProperties(typeof(DerivedClass))["PropertyVirtual"].Attributes[typeof(MyPropertyAttribute)] != null);

However the last one will have a great performance overhead in comparison with the “Attribute.GetCustomAttributes” usage.

I’ve tested the same issue with .NET 4.0 (beta 2) and it is still there. I really hope this article will help you detecting problems with your software and eliminating issues related to this very case.

You can find the source code for this article here: MetadataIssue

Advertisements

2 thoughts on “Reflection Limitation in .NET 3.5/4.0

  1. Hi,
    This is interesting stuff you describe here.
    Do you know the reason for this design decision? I’m sure this is not an accident (at least I hope so).

    Ido.

    • Hi Ido, actually I think this is a bug. I’ve never seen warnings in MSDN that discourage developers combining this reflection usecase with virtual properties. Also according to the Reflector the procedures used to fetch custom attributes via MemberInfo and Attribute seem to be quite different (not re-using some shared codebase) so I think they were written by different developers and there’s still a probability that a bug was made. Anyway this is a very serious issue for scalable enterprise applications and should be mentioned in bold font all over the MSDN I guess.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s