views:

406

answers:

3
// Cannot change source code
class Base
{
    public virtual void Say()
    {
        Console.WriteLine("Called from Base.");
    }
}

// Cannot change source code
class Derived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Derived.");
        base.Say();
    }
}

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

class Program
{
    static void Main(string[] args)
    {
        SpecialDerived sd = new SpecialDerived();
        sd.Say();
    }
}

The result is:

Called from Special Derived.
Called from Derived. /* this is not expected */
Called from Base.

How can I rewrite SpecialDerived class so that middle class "Derived"'s method is not called?

UPDATE: The reason why I want to inherit from Derived instead of Base is Derived class contains a lot of other implementations. Since I can't do "base.base.method()" here, I guess the best way is to do the following?

// Cannot change source code

    class Derived : Base
    {
        public override void Say()
        {
            CustomSay();

            base.Say();
        }

        protected virtual void CustomSay()
        {
            Console.WriteLine("Called from Derived.");
        }
    }

    class SpecialDerived : Derived
    {
        /*
        public override void Say()
        {
            Console.WriteLine("Called from Special Derived.");
            base.Say();
        }
        */

        protected override void CustomSay()
        {
            Console.WriteLine("Called from Special Derived.");
        }
    }
+7  A: 

You can't from C#. From IL, this is actually supported. You can do a non-virt call to any of your parent classes... but please don't. :)

rh
+1  A: 

The answer (which I know is not what you're looking for) is:

class SpecialDerived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

The truth is, you only have direct interaction with the class you inherit from. Think of that class as a layer - providing as much or as little of it or its parent's functionality as it desires to its derived classes.

EDIT:

Your edit works, but I think I would use something like this:

class Derived : Base
{
    protected bool _useBaseSay = false;

    public override void Say()
    {
        if(this._useBaseSay)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

Of course, in a real implementation, you might do something more like this for extensibility and maintainability:

class Derived : Base
{
    protected enum Mode
    {
        Standard,
        BaseFunctionality,
        Verbose
        //etc
    }

    protected Mode Mode
    {
        get; set;
    }

    public override void Say()
    {
        if(this.Mode == Mode.BaseFunctionality)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}

Then, derived classes can control their parents' state appropriately.

JoshJordan
+11  A: 

This is a bad programming practice, and not allowed in C#. It's a bad programming practice because

  • The details of the grandbase are implementation details of the base; you shouldn't be relying on them. The base class is providing an abstraction overtop of the grandbase; you should be using that abstraction, not building a bypass to avoid it.

  • You derived from your base because you like what it does and want to reuse and extend it. If you don't like what it does and want to work around it rather than work with it, then why did you derive from it in the first place? Derive from the grandbase yourself if that's the functionality you want to use and extend.

  • The base might require certain invariants for security or semantic consistency purposes that are maintained by the details of how the base uses the methods of the grandbase. Allowing a derived class of the base to skip the code that maintains those invariants could put the base into an inconsistent, corrupted state.

Eric Lippert
On the topic of security, it should be noted that you cannot guarantee that callers will call the most-derived version of a virtual function.
rh
@rh: yes, that is a good point. Hostile code does not need to obey the rules of C#.
Eric Lippert
@rh: In fact, it was worse before .NET 2.0; it used to be legal in IL to call overridden methods on a base class even if the calling class wasn't derived from it. See http://research.microsoft.com/en-us/um/people/akenn/sec/appsem-tcs.pdf for comments on how this and other IL/C# mismatches affect the security of C# code.
kvb
+1 OOP is meant exactly to reuse a basic behaviour from a base class. If one rather do what the base.base class does, then derive from it, instead of deriving from its derived. =)
Will Marcouiller
@kvb: Excellent point. In fact this change was make right before 2.0 shipped, and it broke verifiability of base calls in anonymous methods in C#. As you note, the verifier checks that the call site is *derived from* the type declaring the method; it does *not* check if the call site is *contained within* the type declaring the method! Fortunately the code generator now does the right thing, at long last, and we have eliminated the "base call is not verifiable" warning.
Eric Lippert