Saturday, April 9, 2011

Interfaces and inheritance

 

Quite often, we would like to reflectively use properties defined on types.

Basic Reflection

The Type class has a GetProperties() method which returns all the properties on it.

var properties = typeof(Foo).GetProperties()


Consider the following inheritance hierarchy:
internal class Base
{
public string BaseProperty { get; set; }
}

internal class Derived : Base
{
public string DerivedProperty { get; set; }
}

var properties = typeof(Derived).GetProperties();

We expect the properties collection to have two items corresponding with BaseProperty and DerivedProperty respectively.


Figure1


The Trouble with Interfaces


Now consider the following inheritance hierarchy:

internal interface IBase
{
string BaseProperty { get; set; }
}

internal interface IDerived : IBase
{
string DerivedProperty { get; set; }
}

var properties = typeof(IDerived).GetProperties();

Do we expect anything different? Running this gives us:


Figure2


Now, there is only one property: the DerivedProperty on the IDerived interface – why?


Legalese…


It turns out that this behaviour is by design, and it hinges on the way in which inheritance is interpreted by the language designers.


This is made clear in section 8.10 of the ECMA CLI specifications where it says (emphasis mine):

8.10 Member inheritance Only object types can inherit implementations, hence only object types can inherit members (see §8.9.8). While interface types can be derived from other interface types, they only “inherit” the requirement to implement method contracts, never fields or method implementations.

Reading through the legalese, what this means is that derived interfaces do not contain their base interface properties like derived classes do – they only specify the requirement that any implementer of the interface must implement both derived and base properties.


It’s a very fine line, but  I’m not immediately aware of why such a line exists!


The way out


We can quite easily write a little extension function to iterate over all the properties of a class, recursively including any base classes and interfaces.


There are a few key points to notice which will be pointed out, but first, the code:

public static IEnumerable<PropertyInfo> GetAllProperties(this Type _this,
IList<Type> processedInterfaces = null,
BindingFlags flags =
BindingFlags.Instance | BindingFlags.Public | BindingFlags.DeclaredOnly)
{
processedInterfaces = processedInterfaces ?? new List<Type>();

if (!processedInterfaces.Contains(_this))
{
foreach (var _pi in _this.GetProperties(flags))
{
yield return _pi;
}

if (_this.BaseType != null)
{
foreach (var _pi in _this.BaseType.GetAllProperties(processedInterfaces, flags))
{
yield return _pi;
}
}

if (_this.IsInterface)
{
foreach (var _pi in _this.GetInterfaces().SelectMany(_ => _.GetAllProperties(processedInterfaces, flags)))
{
yield return _pi;
}
}

processedInterfaces.Add(_this);
}

yield break;
}

The first thing to note is that we treat a single class atomically. That is to say, we consider a class to potentially have a base class and some interfaces. We recursively treat the base class the same way.


However, to prevent double-counting of the base class properties, we limit the properties considered for each class to be just those specified in the class itself.


Furthermore, multiple classes and interfaces can implement the same interface, so we keep a list of interface types that we have processed, and only process them once. This allows for complex interface hierarchies to be expressed in their simplest form. We use a default argument for this to keep the function signature as minimal as possible without sacrificing functionality.


The last bit of elegance is to use the yield operator to only process the enumeration at iteration time. As a bonus, this keeps the code very readable!


Enjoy!

No comments:

Post a Comment