使用接口组合的奇怪的界面多态性

时间:2009-02-13 06:55:59

标签: c# .net interface polymorphism explicit

我在我正在开发的项目中得到了类似下面代码的内容。我认为我被允许这样做真的很奇怪,但是现在我开始想知道我最有可能是一个建筑师,这让我想到了这一点。

我的问题是:

  • 究竟是什么名字?
  • 这有什么现实世界的用途?
  • 为什么有人想要这样做?

以下是我的界面:

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

    }
}

2 个答案:

答案 0 :(得分:5)

此代码完全没有问题(只要它不会让您的用户感到困惑),并且它不是具有我熟悉的任何名称的模式。 CanDoThisAndThat实现了两个接口,因此客户端可以以任何一种方式使用它。

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

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

  1. 两个接口具有相同的成员定义
  2. 您需要实现一个接口,但不希望宣传某个特定成员可用于未使用接口类型声明引用的客户端代码
  3. .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
    {
        // ...
    }
    

答案 1 :(得分:4)

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

在您的情况下,您有三种方法,三种方法中的每一种都对应于其中一个接口中的方法。

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

如果您的签名只有返回类型不同,或者您实现的两个异构接口碰巧具有相同的方法名称但应该执行不同的操作,则有时可能需要使用它。无论你在哪里可以避免它,做!它使代码非常容易混淆。