Заставить метод родительского класса вызывать переопределенные методы вместо дочерних переопределений - PullRequest
0 голосов
/ 30 ноября 2018
public class C1 {
    public virtual void A() { Console.WriteLine("Good"); }
    public void B() { A(); } // <-- wrong A when when called in child classes :(
}
public class C2 : C1 {
    public override void A() { B(); }
}

Могу ли я что-нибудь сделать в C2 так, чтобы, когда C2.A () вызывает B (), следующий вызов был C1.A () вместо C2.A (), что приводит к бесконечной рекурсии?

Ответы [ 4 ]

0 голосов
/ 04 декабря 2018

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

public class C1 {
    public virtual void A() { Console.WriteLine("Good"); }
    public void B() { A(); }
}
public class C2 : C1 {
    public override void A()
    {
        var method = new System.Diagnostics.StackTrace()
                    .GetFrames().Skip(1).Select(f => f.GetMethod())
                    .FirstOrDefault(m => m.Name == nameof(A) && m.DeclaringType == typeof(C2));
        if (method == null) B();
        else base.A();
    }
}

Или, как я уже сказал, вы можете просто проверить, если

var method = new System.Diagnostics.StackTrace().GetFrame(2).GetMethod();
if (method.Name != nameof(A)) B();
else base.A();

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

0 голосов
/ 30 ноября 2018

Мне пришлось переименовать ваши классы и методы - As, B и C сводили меня с ума.

ПРИМЕЧАНИЕ: Это повтор, после вашего комментария, что базовый класс нужно было оставить в покое.Я думаю, что это отвечает на ваш вопрос.Это глупо, но я думаю, что это должно работать:

public class Base {
    public virtual void TheVirtualFunction() {
        Console.WriteLine($"Base Class Implemenation of Virtual Function from type {GetType().Name}");
    }
    public void NonVirtualFuction() {
        Console.WriteLine($"The non-Virtual Function - Calling Vitual function now from type {GetType().Name}");
        TheVirtualFunction();
    }
}

public class Sub : Base
{
    private bool _isCallingAgain = false;
    public override void TheVirtualFunction()
    {
        Console.WriteLine($"Sub Class Implemenation of Virtual Function from type {GetType().Name}");
        if (_isCallingAgain)
        {
            Console.WriteLine($"Skipping because v-func called twice from type {GetType().Name}");
            return;
        }
        _isCallingAgain = true;
        Console.WriteLine($"This pass through the virtual function does something (from type {GetType().Name})");
        NonVirtualFuction();
        _isCallingAgain = false;
    }
}

Тогда в моей функции Main у меня есть код, который выглядит следующим образом:

static void Main(string[] args) {
    var theBase = new Base();
    Console.WriteLine("Calling Base Class Non Virtual function");
    theBase.NonVirtualFuction();

    Console.WriteLine("\r\nCalling Base Class Virtual function directly");
    theBase.TheVirtualFunction();

    var theSub = new Sub();
    Console.WriteLine("\r\nCalling Non-Virtual Function (implemented in the base class) using sub-class reference");
    theSub.NonVirtualFuction();

    Console.WriteLine("\r\nCalling Sub Class Virtual function directly");
    theSub.TheVirtualFunction();

    Console.ReadLine();
}

Результат всего, чтоэто выдает консоль:

Calling Base Class Non Virtual function
The non-Virtual Function - Calling Vitual function now from type Base
Base Class Implemenation of Virtual Function from type Base

Calling Base Class Virtual function directly
Base Class Implemenation of Virtual Function from type Base

Calling Non-Virtual Function (implemented in the base class) using sub-class reference
The non-Virtual Function - Calling Vitual function now from type Sub
Sub Class Implemenation of Virtual Function from type Sub
This pass through the virtual function does something (from type Sub)
The non-Virtual Function - Calling Vitual function now from type Sub
Sub Class Implemenation of Virtual Function from type Sub
Skipping because v-func called twice from type Sub

Calling Sub Class Virtual function directly
Sub Class Implemenation of Virtual Function from type Sub
This pass through the virtual function does something (from type Sub)
The non-Virtual Function - Calling Vitual function now from type Sub
Sub Class Implemenation of Virtual Function from type Sub
Skipping because v-func called twice from type Sub

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

Защитный бит (_isCallingAgain) является частной переменной-членом экземпляра.К нему обязательно обращаются в одном потоке выполнения (путь выполнения входит в v-func Sub, переходит в не виртуальную функцию Base, а затем снова возвращается в v-func Sub).Нет необходимости охранять его с lock или другим (кроме того, чтобы убедиться, что это private).

Я думаю, что это (или вариант этого) - ваш единственный выбор.Такая защита от повторного входа - довольно распространенная модель (она была очень распространена в то время, когда я занимался программированием пользовательского интерфейса Windows на MFC и Win32).

0 голосов
/ 30 ноября 2018

Разве этого не достаточно?Переместите реализацию A в отдельный частный метод в базовом классе, а затем вызовите его вместо базового класса?

public class C1 {
    private void AImplementation() { Console.WriteLine("Good"); }
    public virtual void A() { AImplementation(); }
    public void B() { AImplementation(); }
}
public class C2 : C1 {
    public override void A() { B(); }
}
0 голосов
/ 30 ноября 2018

Полезный человек в IRC предоставил это хакерское решение, не поддерживающее потоки:

public class C1 {
    public virtual void A() { Console.WriteLine("Good"); }
    public void B() { A(); }
}
public class C2 : C1 {
    bool _aOnCallStack = false;
    public override void A() { 
        if (!_aOnCallStack) {
            _aOnCallStack = true;
            B(); 
        }
        else {
            base.A();
            _aOnCallStack = false;
        }
    }
}
...