Как вызвать (не виртуально) оригинальную реализацию виртуального метода? - PullRequest
7 голосов
/ 31 июля 2010

У меня следующая ситуация:

В сторонней библиотеке (не может быть изменено):

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

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

В моем собственном коде:

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

Из реализации C метода M я хочу вызвать A (но не B!).Могу ли я?

Любые трюки принимаются, включая отражение.Я уже пробовал рефлексию, но использование MethodInfo, полученное от typeof(A), все еще генерирует виртуальный вызов (вызов реализации C с последующим переполнением стека).

Получение C из A не может быть и речи из-за сложности переопределения B.

Ответы [ 4 ]

16 голосов
/ 31 июля 2010

вы можете сгенерировать динамический метод для создания прокси, который использует инструкцию Call (не CallVirt)

        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);
1 голос
/ 31 июля 2010

В моем предыдущем ответе я упустил тот факт, что A и B находятся во внешней библиотеке и не могут быть изменены.В этом случае я бы предложил другой подход.По сути, если недостаток дизайна в B, вы не можете использовать B. Подкласс от A.

Печальным последствием этого, конечно, является то, что вам может потребоваться переопределить некоторые или все функциональные возможности.в B. Вы можете при необходимости скопировать код из Reflector.Я понимаю, что это звучит нежелательно, но я все же думаю, что предпочтительнее использовать немодифицируемый код, имеющий известную проблему, которая вызывает у вас проблемы.

1 голос
/ 31 июля 2010

Боюсь, что это невозможно напрямую, как вы описываете - цель виртуальных методов в том, чтобы переопределение было прозрачным.Таким образом, единственный способ сделать это вообще - обойти это решение.

Позвольте мне попытаться это сделать, но учтите, что это хакерское предложение.Если вам действительно нужна эта конструкция в вашем коде, это может указывать на то, что ваш код имеет фундаментальный недостаток дизайна где-то еще, поэтому реструктуризация чего-либо может быть более желательной, чем заполнение его еще одним недостатком дизайна.Но в любом случае, здесь идет ...

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(); }
}
0 голосов
/ 31 июля 2010

Вы не можете этого сделать. Вам следует , вероятно, проектировать иерархию классов по-другому, потому что выглядит странным, что C наследуется от B, а ведет себя как A.

В любом случае, это может иметь смысл в вашем случае. Затем вы должны сделать другой метод в A, который вы не будете переопределять:

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

Кстати, если вы называете метод, как я сделал в примере, то вам, вероятно, следует переосмыслить все это. Если эта иерархия оправдана, то basicM, вероятно, выполняет что-то, что заслуживает того, чтобы быть отдельным методом с другим именем, возможно, даже публичным методом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...