4

Consider this code sample:

public abstract class Parent
{
    public int val;
    public Parent()
    {
        val = 0;
    }
    public virtual void foo()
    {
        inc();
    }

    public virtual void inc()
    {
        val = val + 10;
    }
}

public class Child : Parent
{
    public override void foo()
    {
        base.foo();
    }

    public override void inc()
    {
        val++;
    }
}

static void Main(string[] args)
{
    Parent p = new Child();
    Console.WriteLine("p.val = " + p.val);  //Output: p.val = 0
    p.foo();
    Console.WriteLine("P.val = " + p.val);  //Output: p.val = 1
}

I am assuming the inc() of the Parent class did not get called because {this} pointer is actually pointing to a Child object so the Child's version of inc() will be called from the Parent object's function foo(). Is there a way to force the Parent's function foo() to always call parent's function inc() Like you could in C++ with :: operator?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Nickolay Kondratyev
  • 4,959
  • 4
  • 25
  • 43
  • 1
    What if Parent.inc() is not declared as "virtual"? – niaher Feb 22 '12 at 12:05
  • 1
    Isn't this defeating the whole point of having a polymorphic foo() ? Even if you did Parent::foo() { this.inc() } it will still call Child::foo – StuartLC Feb 22 '12 at 12:05
  • 1
    I'm pretty sure you can do it with reflection. And of course the `base` keyword, which doesn't support everything C++'s `::` supports. – CodesInChaos Feb 22 '12 at 12:08
  • 1
    Your question is "I want non-virtual dispatch of a virtual function". If non-virtual dispatch is what you want then *why did you mark it as virtual in the first place?* – Eric Lippert Feb 22 '12 at 15:42
  • Please don't prefix your titles with "C#" and such. That's what the tags are for. – John Saunders Feb 22 '12 at 15:56

4 Answers4

7

No, the only way you can call a virtual method non-virtually is with base.Foo. Of course, you could write a non-virtual method in Parent, and make Parent.foo() call that, as well as the default implementation of Parent.inc().

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
6

You're over-thinking the problem.

  • If you want non-virtual dispatch then don't make the methods virtual in the first place.

  • If you want both virtual and non-virtual dispatch then make two methods, one virtual and one static

For example:

class Base
{
    protected static void NonVirtualFoo(Base b)
    {
        // Whatever
    }
    public virtual void Foo()
    {
        Base.NonVirtualFoo(this);
    }
}

class Derived : Base
{
    protected new static void NonVirtualFoo(Derived d)
    {
        // Whatever
    }
    public override void Foo()
    {
        Derived.NonVirtualFoo(this);
        Base.NonVirtualFoo(this);
    }
}

Use the right tool for the job. If you want virtual dispatch then call a virtual method. If you want static dispatch then call a static method. Don't try to take a hammer to a virtual method and make it statically dispatched; that's working against the entire purpose of the tool.

Eric Lippert
  • 647,829
  • 179
  • 1,238
  • 2,067
1

The Child instance will call its own type implementation.

foo() calls base.foo() and base.foo() calls inc(), which in this case inc() is from the Child, since the instance is Child type, and will use this implementation.

Samuel Slade
  • 8,405
  • 6
  • 33
  • 55
Adrian Iftode
  • 15,465
  • 4
  • 48
  • 73
0

Well, it is actually possible as said here:

This does the trick:

public abstract class Parent
{
    public int val;

    public Parent()
    {
        val = 0;
    }

    public virtual void foo()
    {
        MethodInfo method = typeof(Parent).GetMethod("inc");
        DynamicMethod dm = new DynamicMethod("BaseInc", null, new Type[] { typeof(Parent) }, typeof(Parent));
        ILGenerator gen = dm.GetILGenerator();
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Call, method);
        gen.Emit(OpCodes.Ret);

        var BaseInc = (Action<Parent>)dm.CreateDelegate(typeof(Action<Parent>));
        BaseInc(this);
    }

    public virtual void inc()
    {
        val = val + 10;
    }
}

But it's only a proof of concept: it's horrible and totally breaks the polymorphism.

I don't think you can have a valid reason to write this.

Community
  • 1
  • 1
ken2k
  • 48,145
  • 10
  • 116
  • 176
  • Is it really necessary to use codegen and not just use normal reflection with `method.Invoke(this, null)`? – svick Feb 22 '12 at 16:24
  • @svick Yes. `method.Invoke(this, null)` would call the overriden `inc` method. – ken2k Feb 22 '12 at 16:32