Bug tracking, when your grandparent isn’t in your family tree
We got a failing test because of some changes we made in RavenDB, and the underlying reason ended up being this code:
The problem was that the type that I was expecting did inherit from the right stuff. See this:
So something here is very wrong. I tracked this until I got to:
return RuntimeTypeHandle.CanCastTo(fromType, this);
And there is stopped. I work around this issue by using IsSubclassOf, instead of IsAssignableFrom.
The problem with IsAssignableFrom is that it is a confusing method. The parent is supposed to be the target, and the type you check is the parameter, but it is very easy to forget that and get confused. This worked for 99% of cases, because the single assembly we usually use also contained the RavenBaseApiController (which obviously can be assigned to itself), so that looked like it worked. IsSubclassOf is much nicer, but you need to understand that this won’t work for interfaces, or check direct equality. In this case, this was exactly what I needed, so that worked.
Which is why many, many projects end up with an extension method called "CanBeCastTo"
Yes, I almost always write an extension-method that reads IsAssignableTo which simply puts types in a human-sensible order.
IsAssignableFrom is one of those methods in the BCL that makes me wonder whether it was designed to solve a problem other than the one it is used for all the time. I don't (want to) believe that an API with real thought applied to it could make such a design mistake as expecting the operation to be called "Yoda-style" to achieve the common test of "is an object of Type A assignable to a variable of Type B".
I would love to hear whether there's a story here, or if it really is just a blunder where somebody designing the API felt this way was correct to them.
Personally, I'm relieved to know it's not just me. :)
I've heard someone say that this API is the programming equivalent of the USB A connector. That pretty much sums it up: it's never oriented right on the first try.
This is just a guess, but my presumption is that it is written that way so that it reads like "A a = b;" Not that that makes it any less confusing, but it does seem more sensible, especially if the author was a compiler/CLR writer.
Whenever I need to use
IsAssignableFrom, I always create the extension method
IsAssignableToto reverse the parameters and make it intuitive. I completely agree that the default is reverse of intuition and logic.