【问题标题】:Why to Use Explicit Interface Implementation To Invoke a Protected Method?为什么要使用显式接口实现来调用受保护的方法?
【发布时间】:2010-09-21 18:48:52
【问题描述】:

在codeplex 中浏览 ASP.NET MVC 源代码时,我发现有一个类显式实现接口是很常见的。显式实现的方法/属性然后调用另一个具有相同名称的“受保护的虚拟”方法/属性。

例如,

public class MvcHandler : IHttpHandler, IRequiresSessionState 
{
    protected virtual bool IsReusable 
    {
        get 
        {
           return false;
        }
    }

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

我现在确定这种编程有什么好处。对我来说,我更喜欢只隐式实现接口 IHttpHandler。

我猜作者只是不希望 MvcHandler 有一个公共属性 IsResuable。 IsReusable 属性只能在 MvcHandler 的实例被视为 IHttpHandler 时使用。不过,我不确定作者为什么会这样。

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

【问题讨论】:

  • 这很有趣,在您最初发帖后的 3 年之前阅读 MVC 源代码,我也有完全相同的问题。

标签: c# interface implicit explicit-interface


【解决方案1】:

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

显式接口实现除了调用受保护的方法之外什么都不做可以避免这个问题,因为派生类可以通过覆盖虚拟方法来改变接口方法的行为,同时保留调用基实现的能力,因为它认为合适.恕我直言,C# 应该有一个显式接口实现产生一个具有 CLS 兼容名称的虚拟方法,所以有人用 C# 编写显式实现IFoo.Bar 的类的派生类可以说override void IFoo.Bar,而有人用其他语言编写可以说,例如Overrides Sub Explicit_IFoo_Bar();因为任何派生类都可以重新实现IFoo.Bar,并且由于任何不重新实现IFoo.Bar的派生类都可以自行调用它,所以我认为密封显式实现没有任何有用的目的.

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

【讨论】:

    【解决方案2】:
    1. 当显式实现成员时,不能通过类实例访问,只能通过接口实例访问。参考:Explicit Interface Implementation Tutorial
    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
        }
    }
    

    【讨论】:

    • 您的理由 #2 实际上是一个非常好的理由,但经常被忽视。如果用户在您的类上使用隐式接口并且您更改了接口,用户仍将调用旧方法。使用显式接口,这是不可能发生的。
    【解决方案3】:

    嗯,不是特定于 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() 实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-05
      • 2010-09-29
      • 2010-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多