【问题标题】:Freakishly weird interface polymorphism using interface composition使用接口组合的奇怪的接口多态性
【发布时间】:2009-02-13 06:55:59
【问题描述】:

在我正在处理的项目中,我最终得到了类似于以下代码的内容。我认为我被允许这样做真的很奇怪,但现在我开始想知道最有可能是我的建筑失误导致我这样做。

我的问题是:

  • 这到底叫什么?
  • 这在现实世界中有什么用途?
  • 为什么会有人想要这样做?

这是我的接口:

namespace ThisAndThat
{
    public interface ICanDoThis
    {
        string Do();
    }

    public interface ICanDoThat
    {
        string Do();
    }

    public interface ICanDoThisAndThat : ICanDoThis, ICanDoThat
    {
        new string Do();
    }
}

这是我的具体课程:

namespace ThisAndThat
{
    public class CanDoThisAndThat : ICanDoThisAndThat
    {
        public string Do()
        {
            return "I Can Do This And That!";
        }

        string ICanDoThis.Do()
        {
            return "I Can Do This!";
        }

        string ICanDoThat.Do()
        {
            return "I Can Do That!";
        }
    }
}

还有我通过的测试:

using Xunit;

namespace ThisAndThat.Tests
{
    public class ThisAndThatTests
    {
        [Fact]
        public void I_Can_Do_This_And_That()
        {
            ICanDoThisAndThat sut = new CanDoThisAndThat();

            Assert.Equal("I Can Do This And That!", sut.Do());
        }

        [Fact]
        public void I_Can_Do_This()
        {
            ICanDoThis sut = new CanDoThisAndThat();

            Assert.Equal("I Can Do This!", sut.Do());
        }

        [Fact]
        public void I_Can_Do_That()
        {
            ICanDoThat sut = new CanDoThisAndThat();

            Assert.Equal("I Can Do That!", sut.Do());
        }

    }
}

【问题讨论】:

    标签: c# .net interface polymorphism explicit


    【解决方案1】:

    这段代码绝对没有问题(前提是它不会让您的用户感到困惑),而且它不是我熟悉的任何名称的模式。 CanDoThisAndThat 实现了两个接口,所以客户端可以任意使用它。

    .NET 允许以这种方式实现接口——称为显式接口实现

    显式接口实现在以下情况下很有用:

    1. 两个接口具有相同的成员定义
    2. 您需要实现一个接口,但不想公开某个特定成员可用于未使用该接口类型声明引用的客户端代码

    .NET 框架中案例 2 的示例是 ICollection.SyncLockList<T> 实现 ICollection 但以下代码将无法编译,因为该成员已被故意“隐藏”,因为 BCL 的设计者不再提倡以这种方式锁定集合:

    List<object> list = new List<object>();
    
    lock (list.SyncRoot) // compiler fails here
    {
        // ...
    }
    

    这种格式的任何遗留代码仍然可以工作,因为引用的类型是ICollection 明确:

    ICollection list = new List<object>();
    
    lock (list.SyncRoot) // no problem
    {
        // ...
    }
    

    【讨论】:

    • 谢谢,给你答案,因为你提供了正在发生的事情的确切名称。
    • 投反对票,因为您的答案有很多错误。首先,CanDoThisAndThat 类实现了三个接口,而不是两个。 ICanDothisAndThat 指出,在实现时,您还必须实现另外两个接口。此外,SyncRoot 没有被实现明确定义以隐藏它以阻止使用,它通常是为了将很少使用的成员保持在站点之外,或者如果实现接口的事实并不重要。看看msdn.microsoft.com/en-us/library/…msdn.microsoft.com/en-us/library/bb356596.aspx
    • 该文档中没有任何地方说您不应该使用 SyncLock,也没有将它们标记为过时,这是在不再使用某些东西时真正要做的事情。
    • @Andy SyncRoot 属性在一段时间以来一直被微软高级工程师视为有问题的设计。看看 Brad Abrams 的 this post,他在其中明确表示“请放心,我们在构建这些集合的通用版本时不会犯同样的错误。”
    • @RichTebb,也不是完全的谴责。那个博客是从 2003 年开始的,但 SyncRoot 一直保留到今天并没有被淘汰。无论如何,让它显式的原因不是为了隐藏它。它是在 1.1 中完成的,这是我能找到的最古老的框架文档。我只能假设它也在 1.0 中明确实现,更有可能允许我们提供一种方法来提供我们自己的 SyncRoot 实现。说这样做是为了阻止使用是错误的。 msdn.microsoft.com/en-us/library/…
    【解决方案2】:

    每种类型都有一个interface mapping(如果您想通过反射查看它,可以使用Type.GetInterfaceMap 检索它)。这基本上是说,“当接口 Y 上的方法 X 被调用时,这个方法 Z 就是要调用的方法。”请注意,即使 C# 不支持它,映射目标方法的名称也可能与接口方法名称不同! (我相信,VB 明确支持这一点。)

    在您的情况下,您有三个方法,这三个方法中的每一个都对应于所涉及的接口之一中的一个方法。

    当编译器通过接口发出对虚拟方法的调用时,生成的 IL 会显示类似“在此对象上调用 IFoo.Bar”的内容 - 然后使用接口映射解析 IFoo.Bar。

    如果您的签名仅在返回类型上有所不同,或者您正在实现两个碰巧具有相同方法名称但应该做不同事情的异构接口,您有时可能需要使用它。但是,无论您可以在哪里避免它,都这样做!这使得代码非常混乱。

    【讨论】:

      猜你喜欢
      • 2011-02-20
      • 2019-04-27
      • 1970-01-01
      • 2016-09-12
      • 2017-11-28
      • 2013-05-17
      • 1970-01-01
      • 2011-09-24
      • 2015-11-11
      相关资源
      最近更新 更多