如何调用base.base.method()?

AZ.*_*AZ. 108 c# polymorphism

// Cannot change source code
class Base
{
    public virtual void Say()
    {
        Console.WriteLine("Called from Base.");
    }
}

// Cannot change source code
class Derived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Derived.");
        base.Say();
    }
}

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}

class Program
{
    static void Main(string[] args)
    {
        SpecialDerived sd = new SpecialDerived();
        sd.Say();
    }
}
Run Code Online (Sandbox Code Playgroud)

结果是:

来自Special Derived.
来自Derived./*这不是预期的*/
从Base调用.

如何重写SpecialDerived类,以便不调用中产阶级"Derived"的方法?

更新: 我想继承Derived而不是Base的原因是Derived类包含许多其他实现.既然我不能在base.base.method()这里做,我想最好的方法是做到以下几点?

//无法更改源代码

class Derived : Base
{
    public override void Say()
    {
        CustomSay();

        base.Say();
    }

    protected virtual void CustomSay()
    {
        Console.WriteLine("Called from Derived.");
    }
}

class SpecialDerived : Derived
{
    /*
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
    */

    protected override void CustomSay()
    {
        Console.WriteLine("Called from Special Derived.");
    }
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 89

这是一种糟糕的编程习惯,在C#中是不允许的.这是一个糟糕的编程习惯,因为

  • grandbase的详细信息是基础的实现细节; 你不应该依赖它们.基类提供了grandbase的抽象覆盖; 你应该使用那种抽象,而不是建立旁路来避免它.

  • 您从您的基础派生,因为您喜欢它的功能,并希望重用和扩展它.如果你不喜欢它的功能并希望解决它而不是使用它,那么你为什么首先从中得到它呢?如果这是您想要使用和扩展的功能,请自己从grandbase中获取.

  • 基础可能需要某些不变量用于安全性或语义一致性目的,这些不变量由基本如何使用grandbase方法的细节维护.允许基类的派生类跳过维护这些不变量的代码可能会使基数陷入不一致,损坏的状态.

  • @EricLippert如果基本代码有缺陷并需要像第三方控件一样被覆盖怎么办?并且实现包括对grandbase的调用?对于真实世界的应用程序,它可能具有关键性质并且需要修复,因此等待第三方供应商可能不是一种选择.糟糕的做法与生产环境的现实. (6认同)
  • @Jadoon:然后更喜欢组合继承.您的类可以使用Base或GrandBase的实例,然后该类可以将功能推迟到实例. (4认同)
  • @BlackOverlord:既然你对这个话题有强烈的感受,为什么不在这个有七年之久的问题上写下你自己的答案呢?这样我们都可以从这个主题的智慧中受益,然后你就可以在StackOverflow上写下两个*答案,使你的总贡献翻倍.这是双赢的. (4认同)
  • @Eric利珀特:有两个原因,为什么我没有写我自己的答案:第一,我还不知道怎么办呢,那为什么我发现这个话题.其次,Evk在本页面上有一个全面的答案. (3认同)
  • @DanW:我知道答案; 这是我答案的第一句话:**C#中不允许使用所需的功能,因为这是一个糟糕的编程习惯**.你是怎么用C#做的?**你没有.**我可能不知道这个问题的答案的想法是一个有趣的,但我们会在没有进一步评论的情况下通过.现在,如果你觉得这个答案不能令人满意,为什么不写下你认为做得更好的答案呢?这样我们都可以从你的智慧和经验中学习,你也会将你今年发布的答案数量增加一倍. (3认同)
  • 关于安全性问题,应该注意的是,您不能保证调用者将调用虚拟函数的最派生版本. (2认同)
  • @rh:事实上,在.NET 2.0之前它更糟糕; 它曾经在IL中合法调用基类上的重写方法,即使调用类不是从它派生的.有关此和其他IL/C#不匹配如何影响C#代码安全性的注释,请参见http://research.microsoft.com/en-us/um/people/akenn/sec/appsem-tcs.pdf. (2认同)
  • 问题在于现实世界并不一定总是符合编程模式和最佳实践。关于“你喜欢它所做的事情并且想要重用和扩展它”——确切地说,你喜欢 95% 的“它所做的事情并且想要重用它”,但是你可能不想重用调用 base 时发生的单一事情。方法() (2认同)

Evk*_*Evk 88

只想在这里添加这个,因为人们在很多时间之后仍然会回到这个问题.当然这是不好的做法,但仍然可以(原则上)做作者想要的事情:

class SpecialDerived : Derived
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        var ptr = typeof(Base).GetMethod("Say").MethodHandle.GetFunctionPointer();            
        var baseSay = (Action)Activator.CreateInstance(typeof(Action), this, ptr);
        baseSay();            
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 我真的很喜欢这个答案,因为问题不在于是否推荐,或者是否是一个好主意,问题在于是否有办法实现,如果是这样的话. (36认同)
  • 感谢那!我想提醒其他人不要在回答问题时引用指导方针。被问是有原因的,而不是责骂。如果你不知道,那就不要回答它!我还想鼓励每个人都抨击代码警察,这样可能会阻止他们发布非答案。如果在回答一个问题后,你觉得有必要引用指导方针,那就继续提吧。 (3认同)
  • 没关系,想通了。转换为 `Func<stuff>` 而不是 `Action` (3认同)
  • 当你必须处理缺乏可扩展性的框架/库时特别有用.举个例子,我从来没有需要为NHibernate提供高度可扩展的解决方案.但是为了处理Asp.Net Identity,Entity Framework,Asp.Net Mvc,我经常最终使用这些黑客来处理他们缺少的功能或不适合我需要的硬编码行为. (2认同)
  • 如果我们需要底层函数的返回值怎么办? (2认同)

rh.*_*rh. 23

你不能从C#.从IL,这实际上是支持的.您可以对任何父类进行非virt调用...但请不要.:)


Jos*_*dan 10

答案(我知道的不是你想要的)是:

class SpecialDerived : Base
{
    public override void Say()
    {
        Console.WriteLine("Called from Special Derived.");
        base.Say();
    }
}
Run Code Online (Sandbox Code Playgroud)

事实是,你只与你继承的班级有直接的互动.将该类视为一个层 - 提供与它或其父级功能一样多或少的内容,以及它所希望的派生类.

编辑:

你的编辑工作,但我想我会使用这样的东西:

class Derived : Base
{
    protected bool _useBaseSay = false;

    public override void Say()
    {
        if(this._useBaseSay)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}
Run Code Online (Sandbox Code Playgroud)

当然,在实际实现中,您可能会为可扩展性和可维护性做更多这样的事情:

class Derived : Base
{
    protected enum Mode
    {
        Standard,
        BaseFunctionality,
        Verbose
        //etc
    }

    protected Mode Mode
    {
        get; set;
    }

    public override void Say()
    {
        if(this.Mode == Mode.BaseFunctionality)
            base.Say();
        else
            Console.WriteLine("Called from Derived");
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,派生类可以适当地控制父母的状态.

  • 为什么不在`Derived`中编写一个名为`Base.Say`的受保护函数,以便可以从`SpecialDerived`调用它?更简单,不是吗? (3认同)

小智 7

为什么不简单地将子类转换为特定的父类并调用特定的实现呢?这是一种特殊情况,应该使用特殊情况解决方案.但是,您必须new在children方法中使用关键字.

public class SuperBase
{
    public string Speak() { return "Blah in SuperBase"; }
}

public class Base : SuperBase
{
    public new string Speak() { return "Blah in Base"; }
}

public class Child : Base
{
    public new string Speak() { return "Blah in Child"; }
}

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        Child childObj = new Child();

        Console.WriteLine(childObj.Speak());

        // casting the child to parent first and then calling Speak()
        Console.WriteLine((childObj as Base).Speak()); 

        Console.WriteLine((childObj as SuperBase).Speak());
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 如果您有一个无法了解 base 或 child 的引擎,并且在该引擎调用时说话需要正常工作,那么 speak 需要是一个覆盖,而不是一个新的。如果孩子需要 99% 的 base 功能,但在一种情况下,它需要 superbase 的功能……这就是我理解 OP 要谈论的那种情况,在这种情况下,这种方法将不起作用。这种情况并不少见,引起 C# 行为的安全问题通常并不是很令人担忧。 (2认同)
  • 这不是使用继承,您不妨给每个 Speak 一个完全唯一的名称。 (2认同)

小智 7

public class A
{
    public int i = 0;
    internal virtual void test()
    {
        Console.WriteLine("A test");
    }
}

public class B : A
{
    public new int i = 1;
    public new void test()
    {
        Console.WriteLine("B test");
    }
}

public class C : B
{
    public new int i = 2;
    public new void test()
    {
        Console.WriteLine("C test - ");
        (this as A).test(); 
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 5

您还可以在第一级派生类中创建一个简单的函数,来调用大基函数