【问题标题】:How can I call the 'base implementation' of an overridden virtual method? [duplicate]如何调用被覆盖的虚拟方法的“基本实现”? [复制]
【发布时间】:2010-11-22 23:38:23
【问题描述】:

给定下面的代码,有没有一种方法可以调用类 A 的方法 X 的版本?

class A
{
  virtual void X() { Console.WriteLine("x"); }
}

class B : A
{
  override void X() { Console.WriteLine("y"); }
}

class Program
{
  static void Main()
  {
    A b = new B();
    // Call A.X somehow, not B.X...
  }

【问题讨论】:

    标签: c# oop


    【解决方案1】:

    使用 C# 语言结构,您不能从AB 的范围外部显式调用基函数。如果您确实需要这样做,那么您的设计存在缺陷 - 即该函数一开始不应该是虚拟的,或者应该将部分基本函数提取到单独的非虚拟函数中。

    您可以从内部 B.X 但是调用 A.X

    class B : A
    {
      override void X() { 
        base.X();
        Console.WriteLine("y"); 
      }
    }
    

    但那是另一回事。

    正如 Sasha Truf 在this answer 中指出的那样,您可以通过 IL 来实现。 正如 mhand 在 cmets 中指出的那样,您可能也可以通过反射来完成它。

    【讨论】:

    • (嗨。为了记录,有人删除了我添加的 'stupid-trick's 标签,然后其他人删除了指出它的评论,使它看起来像我真的想要/需要做这个。对于人们似乎想要在 SO 上进行的愚蠢编辑得到通知会很有用。)
    • 从 B 内部调用 A.X() 是我什至可以看到您需要在覆盖后调用它的唯一情况。假设您有一个带有基类成员的复制方法,然后覆盖它以添加新成员的复制功能并在内部调用 base.copy() 以便所有内容都被复制。
    • 有意思,我还以为可以通过反射来完成?
    • 在编写测试时,想要通过多态技巧来修改行为并不罕见。因此,需要从被覆盖的方法中调用基本方法并不一定表明设计不好——它可能表明设计难以测试。正是因为这样的需要,我才看到你的答案。
    • @PéturIngiEgilsson - 当您编写“从被覆盖的方法中调用基本方法”时,我认为您在那里编写的内容与 OP(从在派生类之外。据我了解您的评论,您正在谈论从派生类内部调用该方法,我指出这是可能的。
    【解决方案2】:

    你不能用 C# 来做,但你可以编辑 MSIL。

    Main 方法的 IL 代码:

    .method private hidebysig static void Main() cil managed
    {
        .entrypoint
        .maxstack 1
        .locals init (
            [0] class MsilEditing.A a)
        L_0000: nop 
        L_0001: newobj instance void MsilEditing.B::.ctor()
        L_0006: stloc.0 
        L_0007: ldloc.0 
        L_0008: callvirt instance void MsilEditing.A::X()
        L_000d: nop 
        L_000e: ret 
    }
    

    您应该将 L_0008 中的操作码从 callvirt 更改为 call

    L_0008: call instance void MsilEditing.A::X()
    

    【讨论】:

      【解决方案3】:

      如果方法在派生类中声明为overrides,这是不可能的。为此,派生类中的方法应声明为new

      public class Base {
      
          public virtual string X() {
              return "Base";
          }
      }
      public class Derived1 : Base
      {
          public new string X()
          {
              return "Derived 1";
          }
      }
      
      public class Derived2 : Base 
      {
          public override string X() {
              return "Derived 2";
          }
      }
      
      Derived1 a = new Derived1();
      Base b = new Derived1();
      Base c = new Derived2();
      a.X(); // returns Derived 1
      b.X(); // returns Base
      c.X(); // returns Derived 2
      

      See fiddle here

      【讨论】:

      • 我觉得这应该是这个问题的答案。如果您更改“覆盖”该方法的方式以使其成为具有相同签名的新方法,那么其他一切都应该可以正常工作。
      【解决方案4】:

      我现在知道这是历史问题。但对于其他谷歌用户:你可以写这样的东西。但这需要更改基类,这使得它对外部库毫无用处。

      class A
      {
        void protoX() { Console.WriteLine("x"); }
        virtual void X() { protoX(); }
      }
      
      class B : A
      {
        override void X() { Console.WriteLine("y"); }
      }
      
      class Program
      {
        static void Main()
        {
          A b = new B();
          // Call A.X somehow, not B.X...
          b.protoX();
      
      
        }
      

      【讨论】:

        【解决方案5】:

        您可以这样做,但不是在您指定的时间点。在B 的上下文中,您可以通过调用base.X() 来调用A.X()

        【讨论】:

          【解决方案6】:

          你不能,也不应该。这就是多态性的用途,因此每个对象都有自己的方式来做一些“基础”事情。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2021-11-02
            • 1970-01-01
            • 2017-12-26
            • 2017-06-07
            • 2012-06-15
            • 1970-01-01
            相关资源
            最近更新 更多