31

In the code below I tried in two ways to access the parent version of methodTwo, but the result was always 2. Is there any way to get the 1 result from a ChildClass instance without modifying these two classes?

class ParentClass
{
    public int methodOne()
    {
        return methodTwo();
    }

    virtual public int methodTwo()
    {
        return 1;
    }
}

class ChildClass : ParentClass
{
    override public int methodTwo()
    {
        return 2;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var a = new ChildClass();
        Console.WriteLine("a.methodOne(): " + a.methodOne());
        Console.WriteLine("a.methodTwo(): " + a.methodTwo());
        Console.WriteLine("((ParentClass)a).methodTwo(): "
         + ((ParentClass)a).methodTwo());
        Console.ReadLine();
    }
}

Update ChrisW posted this:

From outside the class, I don't know any easy way; but, perhaps, I don't know what happens if you try reflection: use the Type.GetMethod method to find the MethodInfo associated with the method in the ParentClass, and then call MethodInfo.Invoke

That answer was deleted. I'm wondering if that hack could work, just for curiosity.


8 답변


35

At the IL level, you could probably issue a call rather than a callvirt, and get the job done - but if we limit ourselves to C# ;-p (edit darn! the runtime stops you: VerificationException: "Operation could destabilize the runtime."; remove the virtual and it works fine; too clever by half...)

Inside the ChildClass type, you can use base.methodTwo() - however, this is not possible externally. Nor can you go down more than one level - there is no base.base.Foo() support.

However, if you disable polymorphism using method-hiding, you can get the answer you want, but for bad reasons:

class ChildClass : ParentClass
{
    new public int methodTwo() // bad, do not do
    {
        return 2;
    }
}

Now you can get a different answer from the same object depending on whether the variable is defined as a ChildClass or a ParentClass.


  • Why is using "new" bad in this case ? Is there any case where using "new" is good ? - Peter Huber
  • @PeterHuber because it breaks polymorphism; the main use-case for new in this scenario is for making a type more specialized. For example, DbCommand has a public DbParameterCollection Parameters {get;} - but SqlCommand : DbCommand has a public new SqlParameterCollection Parameters {get;}. Both APIs return the same instance (achieved by a separate protected abstract property, namely DbParameterCollection - so the value will always be a SqlParameterCollection) - but the more-specific type exposes it in a more-specific way - Marc Gravell
  • @MarcGravell: Thanks for the information, although I don't really understand it in detail. For example I don't know if properties can be virtual and overridden. Methods are easier to understand. I came to this question, because my base class needed to call another virtual method in the base and not the overridden one. I got a stackoverflow, because the base class called the overriden version, which called the base class again. By changing override to new everything works fine now. I wish there would be a way to specify that the base virtual method needs to be called, but it seems impossible. - Peter Huber

51

Inside of ChildClass.methodTwo(), you can call base.methodTwo().

Outside of the class, calling ((ParentClass)a).methodTwo()) will call ChildClass.methodTwo. That's the entire reason why virtual methods exist.


  • .., and -1 for all the ways it gets overused/misused. - Michael Meadows
  • Michael - you downvoted someones explanation of polymorphic behavior? Craig was simply explaining why things work the way that they do, don't flail about downvoting good answers simply because they explain a concept you happen to believe is overused. - Andrew Hare
  • I'm guessing he was joking. :) - Craig Stuntz
  • didn't downvote. just wanted to throw in my -1 cent on the overuse of polymorphism. Sorry if that wasn't clear. - Michael Meadows

3

As mentioned above, something is bad with your class design if you need to call "base.base" in PRODUCTION code. But it is quite legitimate to use this technique if you are debugging or searching some workarrounds while using external libraries you cannot compile. It is unpleasant that C# does not provide this option directly. Still you may use Kenneth Xu solution with IL generator and Emit. It works.

class A { public virtual string foo() { return "A"; } }

class B : A { public override string foo() { return "B"; } }

// now in class C
class C : B {}      
// we can call virtual method "foo" from A using following code
MethodInfo fooA = typeof(A).GetMethod("foo", BindingFlags.Public | BindingFlags.Instance);

DynamicMethod baseBaseFoo = new DynamicMethod(
            "foo_A",
            typeof(string),
            new[] { typeof(A) },
            typeof(A));
        ILGenerator il = baseBaseFoo.GetILGenerator();
        il.Emit(OpCodes.Ldarg, 0);
        il.EmitCall(OpCodes.Call, fooA, null);
        il.Emit(OpCodes.Ret);

// call foo() from class A, it returns "A"
(string)baseBaseFoo.Invoke(null, new object[] { this });

For reference and a complete sample see http://kennethxu.blogspot.cz/2009/05/cnet-calling-grandparent-virtual-method.html

Thank you Kenneth Xu!



2

As Mark Gravell said, no, not externally. But here's another hack I have used. ChildClass can expose methodTwo() from the ParentClass for its own use or for external use. In your case:

class ChildClass : ParentClass {
    override public int methodTwo() {
        return 2;
    }
    public int ParentClass_methodTwo() {
        return base.methodTwo();
    }
}

// Now instead of
Console.WriteLine("ParentClass methodTwo: " + ((ParentClass)a).methodTwo());
// use
Console.WriteLine("ParentClass methodTwo: " + a.ParentClass_methodTwo());

In my case, the child class introduced the concept of a Peer, and I needed its override of methodTwo() to invoke the base version on the peer. By overridding it, however, it hid it... Or did it? This technique came to the rescue.

class ChildClass : ParentClass {
    ChildClass peer;
    override public int methodTwo() {
        return peer.ParentClass_methodTwo();
    }
    private int ParentClass_methodTwo() {
        return base.methodTwo();
    }
}


2

public class Parent 
{
    public string Method()
    {
       return "parent";

    }
}

public class Child:Parent
{
    public string Method()
    {
       return "child";

    }
}

Above code successfully overrides parent method yet value of parent method still unchanged.

You can return values from the Both Parent class and Child class using code below -

Child child = new Child();
string result = (((Parent)child).Method()) + child.Method();

But Visual Studio will show you a warning in Compile Time.


  • Do you have a references, It would be better if u do. - gdmanandamohon

1

To my knowledge, once a method has been overridden then you can't call the parent method.


  • This is not a bad answer - I think he meant you can't call the overriden parent method from an instance of the child, which was the spirit of the question. - Andrew Hare

0

I would think that it is not possible unless you make instance of the ParentClass directly. Thats the very essense of inheritance, polymorphism...


0

I stumbled upon this looking for help and ended up with my own approach to calling ancestor versions of a method. This is more of a work-around which assumes you can edit the ancestor class:

Put the functionality in question in the ancestor class into a static method, with the necessary parameters. The method in that class can call it so it need not duplicate the functionality, and the child class can call to that functionality if desired via the static method. That way, it can be done even within a method and can cite which specific class it wants to invoke. Also, it can access farther back ancestors than mere parents, which is the limitation of use "base".

Linked


Related

Latest