Используйте отражение, чтобы вызвать переопределенный базовый метод - PullRequest
18 голосов
/ 05 декабря 2010

Как использовать отражение, вызывать базовый метод, который переопределяется производным классом?

class Base
{
    public virtual void Foo() { Console.WriteLine("Base"); }
}
class Derived : Base
{
    public override void Foo() { Console.WriteLine("Derived"); }
}
public static void Main()
{
    Derived d = new Derived();
    typeof(Base).GetMethod("Foo").Invoke(d, null);
    Console.ReadLine();
}

Этот код всегда показывает 'Derived' ...

Ответы [ 8 ]

33 голосов
/ 05 декабря 2010

Несмотря на то, что текущий ответ уже принят, на самом деле это возможно без необходимости изменения исходного класса с помощью динамического метода, подобного этому:

    static void Main(string[] args)
    {
        Derived foo = new Derived();
        foo.Foo();

        MethodInfo method = typeof(Base).GetMethod("Foo");
        DynamicMethod dm = new DynamicMethod("BaseFoo", null, new Type[] { typeof(Derived) }, typeof(Derived));
        ILGenerator gen = dm.GetILGenerator();
        gen.Emit(OpCodes.Ldarg_1);
        gen.Emit(OpCodes.Call, method);
        gen.Emit(OpCodes.Ret);

        var BaseFoo = (Action<Derived>)dm.CreateDelegate(typeof(Action<Derived>));
        BaseFoo(foo);

        Console.ReadKey();
    }

, как вы можете видеть, это все еще относительно просто сделать

32 голосов
/ 19 января 2013

Через долгое время я наконец нашел лучшее решение, чем DynamicMethod:

class CallOverride
{
    public static void Test()
    {
        var obj = new Override();
        var method = typeof(object).GetMethod("ToString");
        var ftn = method.MethodHandle.GetFunctionPointer();
        var func = (Func<string>)Activator.CreateInstance(typeof(Func<string>), obj, ftn);
        Console.WriteLine(func());
    }
}

class Override
{
    public override string ToString()
    {
        return "Nope";
    }
}

В этом решении используется стандартная подпись конструктора делегата:

public Delegate(object target, IntPtr ftn)

, где target - это целевой экземпляр, а ftn - указатель на функцию. Он напрямую вызывает его с помощью указателя функции базового метода, поэтому делегат будет указывать на фактический базовый метод, а не на переопределенный метод.

4 голосов
/ 05 декабря 2010

Вы не можете сделать это, даже с отражением. Полиморфизм в C # фактически гарантирует, что Derived.Foo() будет вызываться всегда, даже если экземпляр Derived приведен к базовому классу.

Единственный способ вызвать Base.Foo() из экземпляра Derived - явно сделать его доступным из класса Derived:

class Derived : Base
{
    public override void Foo()
    {
        Console.WriteLine("Derived");
    }

    public void BaseFoo()
    {
        base.Foo();
    }
}
2 голосов
/ 28 января 2013

Этого можно достичь с помощью кода emit

http://blogs.msdn.com/b/rmbyers/archive/2008/08/16/invoking-a-virtual-method-non-virtually.aspx

1 голос
/ 05 декабря 2010

Отражение позволяет увидеть, что объект d имеет метод "Foo", а также вызвать его.

Этот метод, однако, является виртуальным методом , и именно поэтому вы получаете реализацию этого метода классом Derived, поскольку это то, что есть d (в дополнение к тому, что литье на базу).

Не существует [прямого] способа вызывать виртуальные методы базы из производного объекта.
Как показано в книге Фредерика Хамиди, метод базового класса может быть предоставлен классом Derived (под другим именем), но это на самом деле не вызывает метод Base, он вызывает метод класса Derived, который вызывает метод Base способ.

Хотя этот подход, заключающийся в том, что класс Derived предоставляет «прокси» для метода базового класса, в конечном итоге делает то, о чем вы просите, вероятно, это плохая идея: вероятно, в вашей структуре есть недостаток объектная модель: это был бы довольно странный вариант использования ...

0 голосов
/ 18 августа 2016

Может быть, Kii искал что-то вроде этого

class Base
{
    public virtual void Foo() 
    { 
        Console.WriteLine("Base"); 
    }
}

class Derived : Base
{
    // Change virtual with new
    // public override void Foo() { Console.WriteLine("Derived"); }
    public new void Foo() 
    { 
        Console.WriteLine("Derived"); 
    }
}

static void Main(string[] args)
{
    Derived d = new Derived();
    d.Foo();// Output: Derived

    typeof(Base).GetMethod("Foo").Invoke(d, null);// Output: Base

    // Or you can cast
    ((Base)d).Foo();// Output: base

    Console.ReadLine();
}
0 голосов
/ 05 декабря 2010
Base b = (Base)d;
Console.WriteLine(b.GetType());  //output:Derived

1) Кастинг не может изменить тип своего класса.

class Derived : Base
{
 public override void Foo() { Console.WriteLine("Derived"); }
 public Base getBase()
 {
  return base; //compiler invalid
 }
}

2) Выше недопустимы, потому что вы никогда не создавали экземпляр объекта Base при создании экземпляра объекта Derived. Вы создали экземпляр объекта Derived class, который унаследован от Base class. Надеюсь, это объясняет, почему вы не смогли вызвать базовую функцию с производным объектом

0 голосов
/ 05 декабря 2010

То, что вы видите, - это полиморфное поведение, которое задумано.Когда вы переопределяете виртуальный метод, вызов этого метода в переопределенном классе вызывает реализацию класса-потомка из VMT.

Каков ваш вариант использования, если честно, это немного пахнет проблемой проектирования.

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