可以转换为未继承的接口吗?

时间:2016-09-30 02:30:04

标签: c#

为什么不能投射一个实例:

while

...到这个界面:

sealed class Foo
{
    public void Go() { }
}

...即使interface IBar { void Go(); } 的签名为Foo

如何将IBar的实例转换为Foo?假设我无法控制IBar

4 个答案:

答案 0 :(得分:25)

不,C#不支持鸭子打字。

此问题的OOP方法是使用the Adapter Pattern

你会这样做:

applicationContext.xml

每当你有一个class FooBarAdapter : IBar { private readonly Foo foo; public FooBarAdapter(Foo foo) { this.foo = foo; } public void Go() { this.foo.Go(); } } 但需要一个Foo时,你可以按需包装它:

IBar

我注意到,如果public void ContrivedScenario() { Foo foo = GetFooFromExternalDependency(); FooBarAdapter adapter = new FooBarAdapter( foo ); this.NeedsIBar( adapter ); } public void NeedsIBar(IBar bar) { ... } - 到 - Foo转换发生很多,您可以使用隐式转换,因此您不需要显式构造IBar个实例,但是如果这是一个很好的软件工程实践,那么这是值得商榷的:

FooBarAdapter

这样你可以这样做:

class FooBarAdapter : IBar {

    // (same as above)

    public static implicit operator FooBarAdapter(Foo foo) {
        return new FooBarAdapter( foo );
    }
}

C#不支持duck-typing的一个原因是因为类的接口(在OOP意义上,不是文字public void ContrivedScenario() { Foo foo = GetFooFromExternalDependency(); this.NeedsIBar( foo ); // the conversion from `Foo foo` to `FooBarAdapter` happens implicitly } )与另一个共享相同的标识符,并不意味着它们是兼容。例如interface(杀死进程)和Process.Kill(杀死所有人类)可能不应该互换使用......除非你真的想要。

答案 1 :(得分:3)

C#没有“鸭子打字”。为了将实例从一种类型转换为另一种类型,它们必须具有继承关系或定义了自定义转换过载。在这种情况下,如果Foo没有被密封,你可以为Foo创建一个简单的子类:

class SubFoo : Foo, IBar {
    void Go()
    {
        base.Go();
    }
}

但是由于Foo本身是密封的,这是不可能的,所以你必须使用像这样的包容。

class ContainsFoo : IBar 
{
     ContainsFoo(Foo foo)
     {
         this.foo = foo;
     }

     void Go() 
     {
         this.foo.Go();
     }
}

答案 2 :(得分:3)

除了@Dai's answer之外,如果你想在测试项目中进行foo-bar演员,可以选择使用a mock framework

var bar = Mock.Of<IBar>();
Mock.Get(bar).Setup(b => b.Go()).Callback(() => foo.Go());

MethodThatNeedsIBar(bar);

框架创建IBarfoo类型的代理,就像适配器一样工作,但它更容易设置,代码更少。

答案 3 :(得分:1)

还有Impromptu-Interface包来处理包裹的鸭子打字。它是一个完善的框架,有多种选择,包括处理集合的能力。