Wednesday, June 3, 2009

Lesson learned....RTFM!

A few weeks ago, I was tasked with writing a site map generator for our new site at work that uses ASP.NET MVC. When using ASP.NET MVC Controllers have "Actions" that map to URL's. For the sake of this blog point, all that's important to know is that generally Actions are methods that return an ActionResult object. So, the idea was, in order to generate the site map correctly, I would use reflection to inspect the entire assembly, and any method that returned an ActionResult would be entered into the site map with the correct XML.

The catch however was that in many instances, the return type of our Action's weren't actually ActionResult objects, but rather classes the derived from ActionResult. (Some that are built into ASP.NET MVC and some custom ones.) So, I quickly realized I needed a method that you can pass in a type, along with another type, and the method would tell you if anywhere up the inheritance chain, type1 inherits from type2.

My first thought was "Perfect! I'll just use recursion!" This quickly got me excited, because it's not every day you get to use recursion in a useful piece of production code, much less when it came to reflection. So, I ended up writing an extension method that crawls up the inheritance chain recursively and it worked perfectly. Here's the code:

public static class TypeExtensions
{
public static bool InheritsFrom(this Type type, Type baseType)
{
if (baseType == null || type == null)
{
return false;
}

if (type.BaseType == baseType)
{
return true;
}

return type.BaseType.InheritsFrom(baseType);
}
}


Basically, the Type class has a handy property called BaseType which basically gives you the BaseType (duh!). If the Type is object, then BaseType is null, in which case we know we've reached the top of the inheritance chain, and we're done. To test this method out, let's whip up some demo code:



public class MyMemoryStreamBase : MemoryStream
{

}

public class MyMemoryStreamChild : MyMemoryStreamBase
{

}

public class MyMemoryStreamGrandChild : MyMemoryStreamChild
{

}
Basically, I've created a nice Inheritance chain that goes up to MemoryStream -> Stream -> MarshalByRefObject and finally object. Here's how you can test it:


Type type = typeof(MyMemoryStreamGrandChild);
bool result = type.InheritsFrom(typeof(MarshalByRefObject));
Console.WriteLine(result);

If you run this, the result will be True, proving that my cute little method worked, and I was all happy.

Fast forward a few weeks, and while browsing StackOverflow, I came across a question regarding reflection and BaseType's when some dude posts a link to this:

Type.IsSubClassOf(..)

Yeah, that's right! It's built right into the .NET Framework! Now obviously it's impossible to know of every method in the framework, but as soon as I thought of the need, and that it involved recursion, I simply whipped out Visual Studio and started coding, without actually taking a moment and thinking, hey research this first! You can't be the FIRST person to come across this very issue!

Feeling completely beat, I was just curious at this point as to how it's implemented in the Framework. Were they also using recursion, or did they have some other ingenious way of doing this. Well here's how they did it:



public virtual bool IsSubclassOf(Type c)
{
Type baseType = this;
if (baseType != c)
{
while (baseType != null)
{
if (baseType == c)
{
return true;
}
baseType = baseType.BaseType;
}
return false;
}
return false;
}

Yea, no recursion, just a simple while loop. At this point, I was just curious in general as to which method is quicker. So, some quick benchmarks:




public void CompareMethodsSpeed()
{
Stopwatch watch = new Stopwatch();
Type type = typeof(MyMemoryStreamGrandChild);
Type baseType = typeof(MarshalByRefObject);
watch.Start();
for (int i = 0; i < 100000; i++)
{
type.InheritsFrom(baseType);
}
watch.Stop();
Console.WriteLine("My way took: {0} ticks.", watch.ElapsedTicks);

watch.Reset();

watch.Start();
for (int i = 0; i < 100000; i++)
{
type.IsSubclassOf(baseType);
}
watch.Stop();
Console.WriteLine("Their way took: {0} ticks.", watch.ElapsedTicks);
}


Then I ran this one my machine and the results were:

My way took: 107433 ticks.
Their way took: 41684 ticks.

So yea, I got my butt whooped!! I'll chalk this one up to experience.

Moral of the story? Two things. First, not just because you CAN do it with recursion, does that mean you SHOULD do it that way. Second, RTFM!! Chances are, MS has more time to test various ways of doing certain things, so double check that something is in the Framework before rolling your own.

The only one thing I'll say in my (somewhat weak) defense is that InheritsFrom is a MUCH better name for this method than IsSubslassOf IMO. So yea, name your methods better MS!!

3 comments:

HeadBoy99 said...

I am also looking for an InheritsFrom method, but I don't want to have to compare my class type to that of an instantiated object; I dopn't have an object of that type. For my use, it seems particularly wasteful to have to instantiate an object just to have a placeholder for the type. I am trying to replicate the Delphi InheritsFrom method that all objects inherit. All you need to pass this method is a class type, e.g. MyObject.InheritsFrom(MyClass)

A.Friedman said...

Both my implementation and the one that's in .NET (IsSubclassOf) work on just the types. IsSubClassOf is a method on Type and takes as a parameter another Type. You don't need to create an object of either Type.

Al the curmudgeon said...

IMHO IsSubclassOf is not as useful as a non-existing method that could be called IsSubclassOfOrIsClass