【问题标题】:Overriding explicit interface implementations?覆盖显式接口实现?
【发布时间】:2016-09-15 14:32:57
【问题描述】:

在子类中重写接口的显式实现的正确方法是什么?

public interface ITest
{
    string Speak();
}

public class ParentTest : ITest
{
    string ITest.Speak()
    {
        return "Meow";
    }
}

public class ChildTest : ParentTest
{
    // causes compile time errors
    override string ITest.Speak()
    {
        // Note: I'd also like to be able to call the base implementation
        return "Mooo" + base.Speak();
    }
}

以上是对语法的最佳猜测,但显然是错误的。它会导致以下编译时错误:

错误 CS0621:

`ChildTest.ITest.Speak()': 虚拟或抽象成员不能 私人的

错误 CS0540:

ChildTest.ITest.Speak()': containing type does not implement interfaceITest'

错误 CS0106:

修饰符 `override' 对该项目无效

我实际上可以在不使用显式接口的情况下让它工作,所以它实际上并没有阻止我,但出于我自己的好奇心,我真的很想知道,如果想使用显式接口执行此操作,正确的语法是什么?

【问题讨论】:

  • 隐式接口实现有什么问题?你想完成什么?
  • @chomba,使用显式接口一直是我代码库中的一种风格选择。我只是认为看一个实现了 4 或 5 个不同接口的类并知道哪些方法与哪些接口相关联要干净得多。我的大多数用例都很好,这是我第一次遇到它们的限制。
  • @JamesMcMahon 据我所知,显式的主要用例是具有相同签名的成员的多个接口。我很少看到它们被使用,如果我这样做了,我会觉得编写代码的人并不经常编写 C#。
  • 是的,这符合程序员的偏好,stackoverflow.com/questions/143405 有很多答案,这些答案涉及每种方法的优缺点。
  • @JamesMcMahon 是的,我同意,但我觉得这是经常使用该语言的人最同意的事情之一。这与看到 var some_object = new my_class() 的 C# 代码感到奇怪的方式相同;蛇案例没有什么问题,但在 C# 中感觉格格不入。

标签: c# oop syntax interface


【解决方案1】:

我也遇到过一种情况,我想重写一个显式接口实现并调用基类,发现这个问题的答案是“无法完成”。

首先要注意的是,为了重写显式接口实现而不调用基类是相当简单的。派生类只需要实现接口本身。

public class ChildTest : ParentTest, ITest
{
  string ITest.Speak()
  {
    return "Mooo";
  }
  // Note: any other interface functions will call ParentTest's implementation
}

然而,现在没有“合法”的方式来调用ParentTestChildTest 类型的对象的ITest.Speak 实现,因为任何使用该接口的尝试都将导致ChildTest 的实现而是被调用。

因此,只有对基本实现的调用会导致复杂性。为了满足我的好奇心,我证明了它可以做到,但实际上它不应该......

保持基类不变,以下内容实际上允许使用反射调用基类。

public class ChildTest : ParentTest, ITest
{
  string ITest.Speak()
  {
    return "Mooo" + typeof(ParentTest).GetMethod("ITest.Speak", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(this, new object[0]) as string;
  }
}

注意,如果示例代码包含在命名空间中,则需要完全限定的接口名称。例如"MyNamespace.ITest.Speak"

如果函数将被重复调用和/或为许多对象调用,则可以通过缓存方法信息和/或为基本调用创建委托来提高性能,例如:

public class ChildTest : ParentTest, ITest
{
  static ChildTest()
  {
    baseSpeakMethodInfo = typeof(ParentTest).GetMethod("ITest.Speak", BindingFlags.Instance | BindingFlags.NonPublic);
  }

  static private MethodInfo baseSpeakMethodInfo;

  public ChildTest()
  {
    baseSpeak = baseSpeakMethodInfo.CreateDelegate(typeof(Func<string>), this) as Func<string>;
  }

  private Func<string> baseSpeak;

  string ITest.Speak()
  {
    return "Mooo" + baseSpeak();
  }
}

与其他答案相比,它的唯一优势是,如果您无法修改基类,它就可以工作。否则,这是一个可怕的解决方案,应该在基类中创建一种机制(与其他答案一样),为派生类提供调用基实现的合法方式。

【讨论】:

    【解决方案2】:

    父级中的public virtual string Speak() 和子级中的public override string Speak() 应该可以正常工作。对于此用例,您不能使用显式接口。您可以通过声明一个受保护的成员并在需要使用它们时在显式接口实现中调用它来解决它。

    【讨论】:

    • 此时不再是显式接口。如果你尝试在显式接口前面添加 virtual 关键字,你会得到 error CS0106: The modifier virtual' is not valid for this item`
    • @JamesMcMahon 您的示例没有理由必须是显式接口;如果这是一个要求,你应该改变你的例子/问题。
    • 更新了问题以澄清事情
    • @JamesMcMahon 显式接口仅在您必须使用具有相同签名的方法实现多个接口时才需要,在这种情况下,答案是创建不需要的方法拥有这些名称并在显式接口实现中调用它们
    【解决方案3】:

    您不能覆盖显式接口实现。它们不能是虚拟的,因此无法直接覆盖它们。但是,您可以通过调用受保护的虚拟成员将它们间接虚拟化:

    public interface ITest
    {
        string Speak();
    }
    
    public class ParentTest : ITest
    {
        string ITest.Speak()
        {
            return Speak();
        }
    
        protected virtual string Speak()
        {
            return "Meow";
        }
    
    }
    
    public class ChildTest : ParentTest
    {
        protected override string Speak()
        {
            return "Mooo";
        }
    }
    

    【讨论】:

      【解决方案4】:

      显式接口实现不能是虚拟成员。请参阅C# language specification 的第 13.4.1 节(它已过时,但此逻辑在 C# 6.0 中似乎没有改变)。具体来说:

      这是显式接口成员的编译时错误 包含访问修饰符的实现,它是一个编译时 包含修饰符 abstract、virtual、override 或 static 时出错。

      这意味着,您将永远无法直接覆盖此成员。

      您可以做的一种解决方法是从您的显式实现中调用另一个虚拟方法:

      class Base : IBla
      {
          void IBla.DoSomething()
          {
              this.DoSomethingForIBla();
          }
      
          protected virtual void DoSomethingForIBla()
          {
              ...
          }
      }
      
      class Derived : Base
      {
          protected override void DoSomethingForIBla()
          {
              ...
          }
      }
      

      【讨论】:

      • 带有文档链接的好答案。我一直在使用显式界面作为风格选择,却没有意识到他们有这个限制。
      • 很棒的答案@Bas!我一直在想办法弄清楚为什么我不能覆盖我的接口实现。谢谢!
      【解决方案5】:

      您可以使用protected virtual 方法,并保持实现非公开,因此您仍然有显式接口实现,它只是实现的包装:

      public class ParentTest : ITest
      {
          protected virtual string Speak_Impl()
          {
              return "Meow";
          }
          string ITest.Speak()
          {
              return Speak_Impl();
          }
      }
      
      public class ChildTest : ParentTest
      {
          protected override string Speak_Impl()
          {
              return "Mooo";
          }
      }
      

      【讨论】:

      • 有意思,没有办法直接重写显式接口实现吗?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-12-04
      • 1970-01-01
      • 1970-01-01
      • 2022-01-01
      • 2020-06-14
      • 2019-03-19
      • 2012-06-15
      相关资源
      最近更新 更多