tags:

views:

167

answers:

4

I have the following situation:

In a 3rd party library (can not be modified):

class A { public virtual void M() {} }

class B : A { public override void M() {} }

In my own code:

class C : B { public override void M() {} }

From C's implementation of method M I want to call A's (but not B's!!). Can I?

Any tricks accepted, reflection included. I tried reflection already, but using the MethodInfo that I get from typeof(A) still generates a virtual call (calling C's implementation with subsequent stack overflow).

Deriving C from A is out of the question due to the complexity of reimplementing B.

+1  A: 

I’m afraid this is not possible directly the way you describe — the purpose of virtual methods is for the overriding to be transparent. So the only way to do this at all is via a workaround.

Let me try to whip one up, but please be aware that this is a hacky suggestion. If you really need this construct in your code, it may be an indication that your code has a fundamental design flaw somewhere else, so restructuring something might be more desirable than filling it with yet another design flaw. But anyway, here goes...

class A {
    public virtual void M() { m_protected(); }
    protected void m_protected() { /* code goes here */ }
}

class B {
    public override void M() { /* code here, possibly invoking base.M() */ }
}

class C {
    public override void M() { m_protected(); }
}
Timwi
Thanks @Timwi. As I say in the question, unfortunately A and B are part of a 3rd party library, so no changes allowed there. And yes, there is a design flaw: in class B! :-)
Mau
+1  A: 

In my previous answer I missed the fact that A and B are in an external library and cannot be modified. In that case, I would suggest a different approach. Basically, if the design flaw is in B, you can’t use B. Subclass from A instead.

The unfortunate consequence of this, of course, is that you may need to reimplement some or all of the functionality in B. You may be able to copy the code from Reflector if necessary. I realise that this sounds undesirable, but I still think it is preferable to using unmodifiable code that has a known issue that causes you problems.

Timwi
This would be nice, but there's a TON of non-trivial code in `B`.
Mau
A: 

You cannot do that. You should probably design your class hierarchy differently, because it looks strange that C inherits from B, while behaving like A.

Anyway, it could make sense in your case. Then you should make another method in A which you will not override:

class A {
    protected virtual void basicM() {}
    public virtual void M() { basicM(); }
}
class C {
    public override void M() { basicM(); }
}

BTW, if you name the method as I did in the example, then you should probably rethink the whole thing. If this hierarchy is justified, than basicM probably performs something that deserves to be a separate method with a different name, perhaps even a public method.

zvonimir
Read the question :'( `A` and `B` can't be touched. I'm not asking 'How can I redesign this?' I'm asking how to call `A.M`.
Mau
If you are going to give me negative points, please provide a comment. Both I and everyone else would benefit from knowing what is wrong.
zvonimir
@Mau Read the first sentence of the answer!
zvonimir
This answer is pretty much identical to my earlier one, so I’m surprised you felt this one deserves a downvote but mine doesn’t.
Timwi
+12  A: 

you can generate dynamic method to make proxy that use Call (not CallVirt) instruction

        var x = new C();
        var m = typeof (A).GetMethod("M");
        var dm = new DynamicMethod("proxy",  typeof (void), new [] {typeof(C)}, typeof (C));
        var il = dm.GetILGenerator();
        il.Emit(OpCodes.Ldarg_0);
        il.Emit(OpCodes.Call, m);
        il.Emit(OpCodes.Ret);
        var action = (Action<C>)dm.CreateDelegate(typeof (Action<C>));
        action(x);
desco
I had thought of IL non-virt call, but didn't think it would be so simple. Let me try :-)
Mau
Brilliant. It works like a charm. Cheers!
Mau
This is clever, but if I saw this being used in production code to work around a limitation of architectural design, it would certainly raise my eyebrows.
Dan Bryant