Есть ли способ вызвать родительскую версию переопределенного метода? (С#.NET)
В приведенном ниже коде я попытался двумя способами получить доступ к родительской версии метода MethodTwo, но результат был всегда 2. Есть ли способ получить 1 результат из экземпляра ChildClass без изменения этих двух классов?
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();
}
}
Обновление ChrisW разместил это:
Из-за пределов класса я не знаю любой простой способ; но, возможно, я не знать, что произойдет, если вы попробуете отражение: используйте Type.GetMethod метод для поиска MethodInfo связанный с методом в ParentClass, а затем вызовите MethodInfo.Invoke
Этот ответ был удален. Мне интересно, может ли этот хак работать, просто ради любопытства.
Ответы
Ответ 1
На уровне IL вы, вероятно, могли бы выпустить call
, а не callvirt
, и выполнить задание, но если мы ограничимся С#; -p (edit). время выполнения останавливает вас: VerificationException
: "Операция может дестабилизировать время выполнения.", удалите virtual
, и он отлично работает, слишком умный пополам...)
Внутри типа ChildClass
вы можете использовать base.methodTwo()
- однако это невозможно извне. Вы также можете перейти на более чем один уровень - поддержка base.base.Foo()
не поддерживается.
Однако, если вы отключите полиморфизм, используя метод скрытия, вы можете получить ответ, но по плохим причинам:
class ChildClass : ParentClass
{
new public int methodTwo() // bad, do not do
{
return 2;
}
}
Теперь вы можете получить другой ответ от одного и того же объекта в зависимости от того, определена ли переменная как ChildClass
или ParentClass
.
Ответ 2
Внутри ChildClass.methodTwo()
вы можете вызвать base.methodTwo()
.
Вне класса вызов ((ParentClass)a).methodTwo())
будет вызывает ChildClass.methodTwo
. Это причина, почему существуют виртуальные методы.
Ответ 3
Как упоминалось выше, что-то плохое в дизайне вашего класса, если вам нужно вызвать "base.base" в коде ПРОДУКЦИИ. Но вполне логично использовать эту технику, если вы отлаживаете или выполняете поиск по некоторым рабочим параметрам при использовании внешних библиотек, которые невозможно скомпилировать. Неприятно, что С# не предоставляет эту опцию напрямую. Тем не менее вы можете использовать решение Kenneth Xu с генератором IL и Emit. Он работает.
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 });
Для справки и полной выборки см.
http://kennethxu.blogspot.cz/2009/05/cnet-calling-grandparent-virtual-method.html
Спасибо, Кеннет Сю!
Ответ 4
Как сказал Марк Гравелл, нет, не внешне. Но вот еще один хак, который я использовал. ChildClass
может выставить methodTwo()
из ParentClass
для собственного использования или для внешнего использования. В вашем случае:
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());
В моем случае дочерний класс ввел понятие Peer, и мне нужно было переопределить methodTwo()
, чтобы вызвать базовую версию на одноранговом узле. Однако, переопределив его, он спрятал его... Или сделал это? Эта техника пришла на помощь.
class ChildClass : ParentClass {
ChildClass peer;
override public int methodTwo() {
return peer.ParentClass_methodTwo();
}
private int ParentClass_methodTwo() {
return base.methodTwo();
}
}
Ответ 5
Насколько я знаю, как только метод был переопределен, вы не можете вызвать родительский метод.
Ответ 6
public class Parent
{
public string Method()
{
return "parent";
}
}
public class Child:Parent
{
public string Method()
{
return "child";
}
}
Выше кода успешно переопределяет родительский метод, но значение родительского метода все равно остается неизменным.
Вы можете возвращать значения из класса Родительский класс и Child с использованием кода ниже -
Child child = new Child();
string result = (((Parent)child).Method()) + child.Method();
Но Visual Studio покажет вам предупреждение во время компиляции.
Ответ 7
Я бы подумал, что это невозможно, если вы не представили экземпляр ParentClass напрямую.
Это самая суть наследования, полиморфизм...
Ответ 8
Я наткнулся на это, обратившись за помощью, и оказался в своем собственном подходе к вызову версий-предков метода. Это скорее работа, которая предполагает, что вы можете редактировать класс предков:
Поместите рассматриваемую функциональность в класс предков в статический метод с необходимыми параметрами. Метод в этом классе может вызвать его, поэтому ему не нужно дублировать функциональность, и дочерний класс может вызвать эту функцию, если это необходимо, с помощью статического метода. Таким образом, это можно сделать даже в рамках метода и может указать, какой конкретный класс он хочет вызвать. Кроме того, он может получить доступ к более отдаленным предкам, чем к простым родителям, что является ограничением использования "базы".