Использовать отражение для вызова переопределенного базового метода

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

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();
}

Этот код всегда показывает "Производный"...

Ответ 1

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

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

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

    public void BaseFoo()
    {
        base.Foo();
    }
}

Ответ 2

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

    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();
    }

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

Ответ 3

После долгого времени я наконец нашел лучшее решение, чем 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 - указатель на функцию. Он непосредственно вызывает его с помощью указателя функции базового метода, поэтому делегат будет указывать на фактический базовый метод, а не на переопределенный метод.

Ответ 5

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

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

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

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

Ответ 6

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

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

Ответ 7

Возможно, 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();
}

Ответ 8

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. Надеюсь, это объясняет, почему вы не могли вызывать базовую функцию с производным объектом