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


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

结果是:

从特殊派生调用。
从派生调用。/ *这不是预期的* /
从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.");
    }
}

正在编辑以匹配您的更新。
JoshJordan

Answers:


106

只想在此处添加此内容,因为即使经过很多时间,人们仍然会回到这个问题。当然,这是不好的做法,但是(原则上)仍然可以执行作者想要的操作:

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

46
我真的很喜欢这个答案,因为问题不是是否建议这样做,还是不是一个好主意,问题是是否有办法做到这一点,如果可以的话。
沙威2015年

3
当您必须处理缺乏可扩展性的框架/库时,此功能特别有用。举例来说,我从来没有必要为高度可扩展的NHibernate求助于此类解决方案。但是对于处理Asp.Net身份,实体框架,Asp.Net Mvc,我经常最终会使用此类黑客来处理其缺失的功能或不适合我的硬编码行为。
弗雷德里克

2
感谢那!我想提一提其他人停止引用准则来回答问题。有人问原因是什么,而不是责骂。如果您不知道,那就不要回答!我还想鼓励所有人爆破代码警察,以免阻止他们发布非答案。如果回答问题后,您感到不得不引述准则,请继续提及。
DanW

1
如果我们需要基础函数的返回值怎么办?
珀金斯

2
没关系,想通了。投稿Func<stuff>代替Action
珀金斯

92

这是一种不良的编程习惯,在C#中是不允许的。这是不好的编程习惯,因为

  • grandbase的详细信息是base的实现详细信息;你不应该依赖他们。基类提供了Grandbase的抽象层。您应该使用该抽象,而不是构建旁路来避免这种抽象。

  • 为了说明上述观点的一个具体示例:如果允许,此模式将是使代码易于遭受脆性基类故障的另一种方式。假设C派生自B从中导出A。在代码中C使用base.base调用的方法A。然后,作者B意识到他们在课堂上投入了太多的精力B,一种更好的方法是使中产阶级B2从中衍生AB从中衍生B2。进行此更改后,in的代码C将调用in B2而不是in中的方法A,因为C的作者假设的实现细节(B即其直接基类是A,永远不会改变。C#中的许多设计决策都是为了减轻各种脆性基础故障的可能性。做出base.base非法决定完全阻止了这种失败模式的这种特殊味道。

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

  • 该库可能出于安全性或语义一致性的目的而需要某些不变式,这些不变式由该库如何使用Grandbase方法的详细信息维护。允许基础的派生类跳过维护那些不变式的代码,可能会使基础进入不一致,损坏的状态。


4
@Jadoon:然后更喜欢继承而不是继承。您的类可以采用Base或GrandBase的实例,然后该类可以将功能推迟到该实例。
埃里克·利珀特

4
@BlackOverlord:既然您对这个话题有强烈的兴趣,为什么不对这个已有七年历史的问题写一个自己的答案呢?这样一来,我们都会从您在该主题上的智慧中受益,然后您将在StackOverflow上写下两个答案,使您的总贡献翻倍。那是双赢。
埃里克·利珀特

4
@Eric Lippert:我没有编写自己的答案有两个原因:首先,我不知道该怎么做,这就是为什么我找到了这个话题。其次,Evk在此页面上有完整的答案。
BlackOverlord

3
@DanW:我知道答案;这是我回答的第一句话:C#中不允许使用所需的功能,因为这是一种不好的编程习惯。您如何在C#中做到这一点?你不知道 我可能不知道这个问题的答案的想法很有趣,但是我们将不加评论地予以通过。现在,如果您发现此答案不令人满意,为什么不编写自己认为更有效的答案呢?这样,我们所有人都将从您的智慧和经验中学习,并且您还将使今年发布的答案数量翻倍。
埃里克·利珀特

11
@EricLippert如果基本代码有缺陷并且需要像第三方控件一样被覆盖怎么办?实现包括对Grandbase的调用吗?对于现实世界的应用程序,它可能具有至关重要的性质,需要进行修复,因此,等待第三方供应商可能不是一个选择。不良做法与实际生产环境。
Shiv

22

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


11

答案(我知道这不是您要找的东西)是:

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

事实是,您只能与您继承的类进行直接交互。将该类视为一个层-根据其派生类的需要,提供或多或少提供其父类的功能。

编辑:

您的编辑有效,但我想我会使用以下内容:

class Derived : Base
{
    protected bool _useBaseSay = false;

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

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

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

这样,派生类就可以适当地控制其父母的状态。


3
为什么不只编写一个受保护的函数Derived来调用Base.Say,以便可以从中调用SpecialDerived呢?更简单,不是吗?
2014年

7

为什么不简单地将子类转换为特定的父类并调用特定的实现呢?这是特例情况,应使用特例解决方案。不过,您将不得不new在子方法中使用关键字。

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

2
如果您有一个不了解基础或子级的引擎,并且在该引擎调用语音时需要正常工作,则语音需要优先,而不是新的。如果孩子需要99%的base功能,但在一个案例中,它需要superbase的功能...这就是我理解OP所要谈论的那种情况,在这种情况下,这种方法将行不通。并不少见,引起C#行为的安全问题通常不是很重要。
沙威2015年

只是在控件和事件调用链的情况下,方法通常受到保护,因此无法像这样访问。
希夫

2
这不是在使用继承,您也可以给每个Speak说一个完全唯一的名称。
Nick Sotiros

是的,它不是100%继承,而是使用父级的接口
Kruczkowski

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

3

您也可以在一级派生类中创建一个简单函数,以调用宏基函数


确实如此,这保留了每个人都非常担心的整个抽象方案,并且突出了抽象方案有时比其值得的麻烦更多的方式。
沙威2015年

3

为此,我的2c功能是实现您需要在工具箱类中调用的功能,并在需要时从任何地方调用它:

// Util.cs
static class Util 
{
    static void DoSomething( FooBase foo ) {}
}

// FooBase.cs
class FooBase
{
    virtual void Do() { Util.DoSomething( this ); }
}


// FooDerived.cs
class FooDerived : FooBase
{
    override void Do() { ... }
}

// FooDerived2.cs
class FooDerived2 : FooDerived
{
    override void Do() { Util.DoSomething( this ); }
}

这确实需要一些有关访问权限的思想,您可能需要添加一些internal访问器方法以简化功能。


1

如果您无权访问派生类的源,但需要除当前方法外的所有派生类的源,那么我建议您也应该做一个派生类并调用派生类的实现。

这是一个例子:

//No access to the source of the following classes
public class Base
{
     public virtual void method1(){ Console.WriteLine("In Base");}
}
public class Derived : Base
{
     public override void method1(){ Console.WriteLine("In Derived");}
     public void method2(){ Console.WriteLine("Some important method in Derived");}
}

//Here should go your classes
//First do your own derived class
public class MyDerived : Base
{         
}

//Then derive from the derived class 
//and call the bass class implementation via your derived class
public class specialDerived : Derived
{
     public override void method1()
     { 
          MyDerived md = new MyDerived();
          //This is actually the base.base class implementation
          MyDerived.method1();  
     }         
}

0

从以前的帖子中可以看出,有人可以辩称,如果需要规避类功能,则类体系结构中有问题。可能是正确的,但是在一个大型的成熟项目上,人们不能总是重组或重构类结构。变更管理的各个级别可能是一个问题,但是在重构后保持现有功能的相同运行并不总是一件容易的事,尤其是在有时间限制的情况下。在一个成熟的项目上,要使各种回归测试在代码重组后不及格,这是一项艰巨的任务。通常会出现模糊的“怪异”。在某些情况下,我们遇到了类似的问题,继承的功能不应执行(或应执行其他操作)。我们在下面遵循的方法 是将需要排除的基本代码放在单独的虚函数中。然后可以在派生类中重写此功能,并排除或更改功能。在此示例中,可以防止在派生类中输出“文本2”。

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

public class Derived : Base
{
    public override void Foo()
    {
        base.Foo();
        Console.WriteLine("Text 1");
        WriteText2Func();
        Console.WriteLine("Text 3");
    }

    protected virtual void WriteText2Func()
    {  
        Console.WriteLine("Text 2");  
    }
}

public class Special : Derived
{
    public override void WriteText2Func()
    {
        //WriteText2Func will write nothing when 
        //method Foo is called from class Special.
        //Also it can be modified to do something else.
    }
}

0

围绕从祖父母类继承一个成员方法,在第二个类中重写它的成员方法,然后从一个孙子类再次调用它的方法,似乎存在很多这样的问题。为什么不将祖父母的成员继承给孙子孙呢?

class A
{
    private string mystring = "A";    
    public string Method1()
    {
        return mystring;
    }
}

class B : A
{
    // this inherits Method1() naturally
}

class C : B
{
    // this inherits Method1() naturally
}


string newstring = "";
A a = new A();
B b = new B();
C c = new C();
newstring = a.Method1();// returns "A"
newstring = b.Method1();// returns "A"
newstring = c.Method1();// returns "A"

看起来很简单。...孙子继承了此处的祖父母方法。考虑一下.....这就是“ Object”及其像ToString()这样的成员如何继承到C#中所有类的方式。我认为Microsoft在解释基本继承方面做得不好。过多关注多态和实现。当我浏览他们的文档时,没有这个基本概念的示例。:(


-2

如果要访问基类数据,则必须使用“ this”关键字,或者使用此关键字作为类的引用。

namespace thiskeyword
{
    class Program
    {
        static void Main(string[] args)
        {
            I i = new I();
            int res = i.m1();
            Console.WriteLine(res);
            Console.ReadLine();
        }
    }

    public class E
    {
        new public int x = 3;
    }

    public class F:E
    {
        new public int x = 5;
    }

    public class G:F
    {
        new public int x = 50;
    }

    public class H:G
    {
        new public int x = 20;
    }

    public class I:H
    {
        new public int x = 30;

        public int m1()
        {
           // (this as <classname >) will use for accessing data to base class

            int z = (this as I).x + base.x + (this as G).x + (this as F).x + (this as E).x; // base.x refer to H
            return z;
        }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.