使用接口组合实现异常奇怪的接口多态性
我最终在我正在从事的项目中得到了类似以下代码的内容。 我觉得我被允许这样做真的很奇怪,但现在我开始想知道我的建筑失误很可能是什么导致了我这样做。
我想问你的问题是:
- 这到底叫什么?
- 它在现实世界中有哪些用途?
- 为什么有人想要这样做?
这是我的接口:
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());
}
}
}
I ended up with something like the following code in a project I'm working on. I thought it was really odd that I was allowed to do it, but now I'm starting wonder what is most likely an architectural gaff on my part led me to this.
My questions to you are:
- What exactly is this called?
- What are some real world uses of this?
- Why would anyone want to do this?
Here are my Interfaces:
namespace ThisAndThat
{
public interface ICanDoThis
{
string Do();
}
public interface ICanDoThat
{
string Do();
}
public interface ICanDoThisAndThat : ICanDoThis, ICanDoThat
{
new string Do();
}
}
Here's my concrete class:
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!";
}
}
}
And my passing tests:
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());
}
}
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
这段代码绝对没有任何问题(只要它不会让您的用户感到困惑),并且它不是我熟悉的具有任何名称的模式。
CanDoThisAndThat
实现两个接口,因此客户端可以以任何一种方式使用它。.NET 允许以这种方式实现接口——称为显式接口实现。
显式接口实现在以下情况下很有用:
案例示例.NET 框架中的 2 是
ICollection.SyncLock
。List
实现了ICollection
,但以下代码将无法编译,因为该成员已被故意“隐藏”,因为 BCL 的设计者不再提倡以这种方式锁定集合:这种格式的任何遗留代码仍然可以工作,因为引用显式地属于
ICollection
类型:There is absolutely nothing wrong with this code (provided it isn't confusing for your users), and it isn't a pattern with any name that I'm familiar with.
CanDoThisAndThat
implements two interfaces, so clients can use it in either way..NET allows interfaces to be implemented this way -- known as explicit interface implementation.
Explicit interface implementation is useful when:
An example of case 2 from the .NET framework is
ICollection.SyncLock
.List<T>
implementsICollection
yet the following code will not compile because the member has intentionally been 'hidden' as the designers of the BCL no longer advocate locking collections in this way:Any legacy code of this format will still work, because the reference is of type
ICollection
explicitly:每种类型都有一个接口映射< /a> (可以使用 Type.GetInterfaceMap 如果你想反思一下)。 这基本上是说,“当调用接口 Y 上的方法 X 时,将调用该方法 Z”。 请注意,即使 C# 中不支持,映射目标方法也可能具有与接口方法名称不同的名称! (我相信 VB 明确支持这一点。)
在您的例子中,您有三个方法,这三个方法中的每一个都对应于所涉及的接口之一中的一个方法。
当编译器通过接口发出对虚拟方法的调用时,生成的 IL 会显示类似“在此对象上调用 IFoo.Bar”的内容 - 然后使用接口映射解析 IFoo.Bar。
如果您的签名仅在返回类型上有所不同,或者您正在实现两个恰好具有相同方法名称但应该执行不同操作的异构接口,则有时可能需要使用它。 无论你在什么地方可以避免它,那就去做吧! 这会导致代码非常混乱。
Each type has an interface mapping (which can be retrieved with Type.GetInterfaceMap if you want to look at it with reflection). This basically says, "When method X on interface Y is invoked, this method Z is the one to call." Note that even though it's not supported in C#, it's possible for the mapping target method to have a different name from the interface method name! (VB explicitly supports this, I believe.)
In your case, you have three methods and each of the three methods corresponds to a method in one of the interfaces involved.
When the compiler issues a call to a virtual method via an interface, the IL generated says something like "call IFoo.Bar on this object" - and IFoo.Bar is then resolved using the interface map.
You may sometimes need to use it if either you have signatures which differ only in return type, or if you're implementing two heterogeneous interfaces which happen to have the same method names but should do different things. Wherever you can avoid it though, do! It makes for very confusing code.