Polymorphic generic poofters

Posted on 2016-06-10

You would expect that a member in a sub-class would always take precedence over a member with the same name in the base class when being invoked? I did too.

A colleague recently showed me an unexpected result in a Java test he did. By being clever in the declarations it appeared that it was possible to circumvent the declared scoping (private, public etc) completely. And although that isn't actually the case, the problem does highlight some intricacies with regards to polymorphic design. Lets look at the test example (copied verbatim to C#):


    public class Deer
    {
        private bool HasHorns() { return false; }

        static void Main(string[] args)
        {
            Deer deer = new ReinDeer();
            Console.WriteLine(deer.HasHorns());

            Console.ReadLine();
        }

    }

    public class ReinDeer : Deer
    {
        public bool HasHorns() { return true; }
    }

So we have Deer and we extend ReinDeer from it. Reindeer instances has horns, while Deer instances doesn't. The output from this code yields "false". Did you expected that? Well, I didn't at first, but it does make sense that you would get that result. First of all, it is important to note that the Main method is inside the base class and has access to the privately scoped method, which is why it even compiles in the first place. But more importantly, we've strictly typed the variable to the Deer class, even though you assign a sub-class instance to it. This is really important and also where I think the surprise or expectation comes from, but the method in the sub-class doesn't actually override (or hide) the one in the base class. So it is perfectly acceptable for the compiler to adhere to the strictly typed variable and call its implementation of the method.

At this point I'd also like to note an often overlooked weakness of generic typing. If we change this line in the Main method to be


            var deer = new ReinDeer();

what do you think we get? We get "true" of course, because it assumes the type from the assignment and thus it executes the sub-class' implementation of the method. Great. But what if it wasn't a straight call to a constructor, but instead something like this?


            var deer = World.GetClosestDeer();

How can you be sure which type your variable will assume throughout the life-cycle of your program? And how can you be sure which result you can expect? Using generics is nice and all, but it presents an awful lot of pitfalls and idiosyncrasies when you have to deal with objects over which you have no control (think a 3rd party class library).

So following on from this, I decided to check out at which point proper, predictable polymorphic behavior manifests itself within the scope of this design. I extended the classes a bit (and moved the Main method out of the Deer class since that doesn't really represent a real-world scenario):


    public class Deer
    {
        public void Dump()
        {
            Console.WriteLine(HasHorns());
            Console.WriteLine(NoseColor());
            Console.WriteLine(HoofPattern());
        }

        private bool HasHorns() { return false; }

        public string NoseColor() { return "Black"; }

        public virtual string HoofPattern() { return "Cleft"; }

    }

    public class ReinDeer : Deer
    {
        public bool HasHorns() { return true; }

        public new string NoseColor() { return "Red"; }

        public override string HoofPattern() { return "Single"; }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            Deer deer = new ReinDeer();
            deer.Dump();

            Console.ReadLine();
        }
    }

There are three scenarios tested here. An example as already discussed, an example of where a base-class method is hidden by the sub-class' implementation, and an example of a straight method override on the sub-class. Executing this code yields the following result:


False
Black
Single

I must be honest that I was surprised by this result. Obviously the "false" we've already covered, and of course anyone with an understanding of Object Orientation will expect the "single". But the fact that the base-class' implementation of NoseColor was executed inspite of explicitly hiding it in the sub-class was unexpected. You can argue that the variable is still strictly typed, but in this case that doesn't hold water in my opinion. For one, both implementations are publicly scoped, and thus the signature is exactly the same. And so because I assign a sub-class instance to the variable I expect it to adhere to the new keyword present in the sub-class declaration.

I agree that using member hiding is questionable practice (even more so than using var), but the MSDN reference is a bit lacking in clarity about when and when not it is applied. Specifically it states that:

A method introduced in a class or struct hides properties, fields, and types that share that name in the base class. It also hides all base class methods that have the same signature.

The actual language specification goes into a bit more depth about this:

Hiding an inherited name is specifically not an error, since that would preclude separate evolution of base classes. For example, the above situation might have come about because a later version of Base introduced an F method that wasn’t present in an earlier version of the class. Had the above situation been an error, then any change made to a base class in a separately versioned class library could potentially cause derived classes to become invalid.

That's great, but it actually follows logically that even in a strictly typed scenario it should not call the base-class implementation (it's similarly scoped) since it is defined to be hidden, and possibly wasn't even there before. I believe that in this specific case the behavior is wrong and will result in undefined program behavior in case of base-class updates. Definitely something to watch out for!

Recently on helloserve

A while ago I wrote about interfaces and how everybody just uses it for IoC these days. This time I want to discuss something with regards to established and advertised IoC patterns and problems I have with it.

It's been a year and a bit since I took delivery of my CX-5. If you're wondering how it has been living with this car, read on. Spoiler alert: It's been pretty fantastic.

Here’s a question for you: how quickly do you think it is best to fail in your code? And at what level do you think it should be handled? My answers to those two questions are immediately, and at no level. Let me explain.