为什么要使用显式接口实现来调用受保护的方法?

10
在浏览codeplex中的ASP.NET MVC源代码时,我发现显式实现接口的类很常见。显式实现的方法/属性然后调用另一个同名的“受保护虚拟”的方法/属性。
例如:
public class MvcHandler : IHttpHandler, IRequiresSessionState 
{
    protected virtual bool IsReusable 
    {
        get 
        {
           return false;
        }
    }

    bool IHttpHandler.IsReusable 
    {
        get 
        {
           return IsReusable;
        }
    }
}

我现在不确定这种编程方式的好处。对我来说,我更喜欢隐式实现 IHttpHandler 接口。

我猜作者只是不希望 MvcHandler 有一个公共属性 IsReusable。属性 IsReusable 只能在将 MvcHandler 实例视为 IHttpHandler 时使用。但我仍然不确定作者为什么要这样做。

有人知道这种接口实现风格的更多好处吗?


2
很有趣,距离你最初的帖子正好3年后阅读MVC源代码时,我也有完全相同的问题。 - ScottS
3个回答

14

这种方法不仅适用于MVC,还允许您保持核心公共API的清晰度。如果存在不同接口/等具有相同名称和签名但意义不同的风险,则此方法也很有用。实际上,这种情况很少见。

它还使您能够在子类中提供返回类型更改的实现:

ICloneable仅为简单起见而选择 - 不要钻牛角尖,事实上它是一个定义不清的接口...一个更好的例子应该是像DbCommand等的东西,它们可以这样做 - 但在简短的示例中很难展示)

class Foo : ICloneable
{
    public Foo Clone() { return CloneCore(); }
    object ICloneable.Clone() { return CloneCore(); }
    protected virtual Foo CloneCore() { ... }
}

class Bar : Foo
{
    protected override Foo CloneCore() { ... }
    public new Bar Clone() { return (Bar)CloneCore(); }
}

如果我们使用公共虚方法,我们将无法override它并在基类中使用new,因为您不能同时执行这两个操作:

class A
{
    public virtual A SomeMethod() { ... }
}
class B : A
{
    public override A SomeMethod() { ... }
    //Error 1   Type 'B' already defines a member called 'SomeMethod' with the same parameter types
    public new B SomeMethod() { ... }
}

使用受保护的虚拟方法,任何用法:

  • Foo.Clone()
  • Bar.Clone()
  • ICloneable.Clone()

都将使用具体类型的正确CloneCore()实现。


2
如果一个类显式实现了 IFoo.Bar,并且一个派生类需要 IFoo.Bar 做一些不同的事情,那么派生类将无法调用该方法的基类实现。一个没有重新实现 IFoo.Bar 的派生类可以通过 ((IFoo)this).Bar() 调用基类实现,但是如果派生类重新实现了 IFoo.Bar(为了改变其行为而必须这样做),则上述调用将转到派生类的重新实现,而不是基类实现。即使使用 ((IFoo)(BaseType)this).bar 也无济于事,因为将引用强制转换为接口类型将丢弃有关被转换引用的类型信息(与实例类型不同)。

仅仅调用受保护的方法来实现显式接口避免了这个问题,因为派生类可以通过重写虚方法来改变接口方法的行为,同时保留根据自己的意愿调用基本实现的能力。在我看来,C# 应该生成一个具有符合 CLS 的名称的虚方法来表示显式接口实现,这样,一个 C# 派生类的编写者就可以说 override void IFoo.Bar,而其他语言的编写者就可以说,例如 Overrides Sub Explicit_IFoo_Bar();由于任何派生类都可以重新实现 IFoo.Bar,而且任何没有重新实现 IFoo.Bar 的派生类都可以在自己身上调用它,我不认为将显式实现封闭起来有任何有用的目的。

顺便说一下,在 vb.net 中,正常的模式只是 Protected Overridable Sub IFoo_Bar() Implements IFoo.Bar,无需单独的虚方法。


1
  1. 当成员被显式实现时,它不能通过类实例访问,而只能通过接口实例访问。参考:显式接口实现教程
  2. 根据我的经验,如果接口实现者显式实现一个接口,当您从接口中删除一个方法时,他将收到编译器错误,而如果他/她隐式实现它,则不会收到通知,该方法将保留在代码中。

原因1的示例:

public interface IFoo
{
    void method1();
    void method2();
}

public class Foo : IFoo
{
    // you can't declare explicit implemented method as public
    void IFoo.method1() 
    {
    }

    public void method2()
    {
    }

    private void test()
    {
        var foo = new Foo();
        foo.method1(); //ERROR: not accessible because foo is object instance
        method1(); //ERROR: not accessible because foo is object instance
        foo.method2(); //OK
        method2(); //OK

        IFoo ifoo = new Foo();
        ifoo.method1(); //OK, because ifoo declared as interface
        ifoo.method2(); //OK
    }
}

你的第二个理由实际上是一个非常好的理由,而且经常被忽视。如果用户在你的类上使用隐式接口,并且你更改了接口,用户仍将调用旧方法。使用显式接口,这种情况就不会发生。 - Abel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接