Wednesday, February 11, 2009

Type.IsAssignableFrom seems backwards

Every time I've ever had to use Type.IsAssignableFrom it has always felt backwards. I usually use this method when checking if a given type implements a specific interface or derives from a certain base class.

This has come up recently when writing Windsor Interceptors and also methods that use reflection to walk my assemblies and automatically register components with my container. In these scenarios I end up with the concrete type in a variable of type Type, while the interface or base class I'm checking for is statically known. In the midst of my "where" clauses like "where type.IsGenericType && type.IsPublic" it's tempting to write "type.IsAssignableFrom(typeof(IMyInterface))", but this is, of course, incorrect. You have to reverse the order to get "typeof(IMyInterface).IsAssignableFrom(type)". There's a small amount of cognitive dissonance for me every time I have to do it.

What I'm really thinking is "make sure 'type' is an 'IMyInterface'. Why not make it so the API reflects that? Here's a little extension method that will let you write "where type.IsA<IMyInterface>()" instead. This reads a lot more naturally to me.


public static class TypeExtensions
{
public static bool IsA<T>(this Type type)
{
return typeof(T).IsAssignableFrom(type);
}
}

Extension Methods and CodeDom

A developer on my current project is creating a code generator and wondered how to emit extension methods. It turns out this is really easy.

All you have to do is emit a System.Runtime.CompilerServices.ExtensionAttribute on your static method and the class that contains it. Then just construct the method like a regular static method where the first parameter is the type you want to add the extension to. For example, to emit something like:

public static class StringExtensions {
public static string SayHi(this string instance)
{ ... }
}

you can emit:

[Extension]
public static class StringExtensions {
[Extension]
public static string SayHi(string instance)
{ ... }
}

Then make sure you reference System.Core.dll in your compiler options and everything will work fine.