views:

252

answers:

2

I have a simple base class and derived class:

class Base
{
    public virtual void Write(int value)
    {
        Console.WriteLine("base int: {0}", value);
    }

    public virtual void Write(string value)
    {
        Console.WriteLine("base string: {0}", value);
    }
}

class Derived : Base
{
    public override void Write(int value)
    {
        Console.WriteLine("derived int: {0}", value);
    }

    public virtual void Write(IEnumerable enumerable)
    {
        Console.WriteLine("derived IEnumerable: {0}", enumerable);
    }

    public virtual void Write(object o)
    {
        Console.WriteLine("derived obj: {0}", o);
    }
}

If I run this:

    static void Main(string[] args)
    {
        Derived d = new Derived();
        Console.WriteLine("derived:");
        d.Write(42);
        d.Write("hello");

        Console.WriteLine("base:");
        Base b = d;
        b.Write(42);
        b.Write("hello");
    }

I get:

derived:
derived obj: 42
derived IEnumerable: hello
base:
derived int: 42
base string: hello

But I am expecting "b.Write(42)" and "d.Write(42)" to be identical. The same for the string case.

What am I not understanding? How can I get the behavior to be what I am expecting given the constraint that I cannot modify "Base"?

UPDATE: See Eric's post.

+1  A: 

string can be implicitly cast to IEnumerable (of chars), but its ToString() still returns the string. Therefore, b.Write("hello"); is resolving the IEnumerable virtual method because it is nearer to the referenced type.

I'll verify, but if you override the string overload in your derived class, it might resolve correctly in the client code.

EDIT

I was wrong, overriding doesn't help. You might have to rename your derived virtuals to avoid collision.

EDIT II

The below does work, but it's uber hackey, and I don't like it. Add this method to Derived:

public new void Write(string value)
{
    base.Write(value);
}
Michael Meadows
In the case of Write(int), I don't want the base to be called at all.
Jeff Moser
+2  A: 

This happens because C# considers methods declared in a type before anything else, including override methods. See: Section 7.3 of the C# spec.

This blog post explains it pretty well, and also explains the reasons.

This highly unintuitive behavior is justified by the following two rules:

  1. Whether or not a method is overridden is an implementation detail that should be allowed to change without breaking client code.
  2. Changes to a base class that don't break an inherited class should not break clients of the inherited class.
gcores
The indirect link to http://blogs.msdn.com/nealho/archive/2006/01/19/515173.aspx was helpful.I guess there's no way to bend this code to do what I want? The only thing I can think of is to change Write(object o) to WriteObject(object o), but that's ugly.
Jeff Moser
Better explanation is at http://blogs.msdn.com/ericgu/archive/2005/12/13/503225.aspx
Jeff Moser