【问题标题】:Why explicit interface implementation?为什么要显式接口实现?
【发布时间】:2010-09-29 08:02:04
【问题描述】:

我最近实现了一个类似的类:

class TestClass : IDisposable
{
    RegistryKey m_key;
    public TestClass()
    {
        m_key = Registry.CurrentUser.OpenSubKey("Software", false);
    }

    public void Dispose()
    {
        // m_key.Dispose();
        IDisposable disp = m_key;
        disp.Dispose();
    }
}

如果我取消对 Dispose 的直接调用的注释,我会收到错误 CS0117(“'Microsoft.Win32.RegistryKey' 不包含 'Dispose' 的定义”)。一些谷歌搜索将我带到this thread,在那里我了解了发生了什么,所以我现在了解了它的机制。 MSDN 文档表明作者更喜欢我调用 Close() 而不是 Dispose(),但没有解释原因。

这种模式的目的是什么(我想我在 IO 类中也看到过)?鉴于这是类作者的有意决定,上面的代码有多糟糕(通过 IDisposable 接口调用 Dispose)?不会太糟糕 - 毕竟,这是在 using 语句中会发生的事情,对吧?

[编辑:1)将标题从“非公开”更改为“显式”2)从我的代码中删除了显式实现,不小心从实验中遗留下来]

【问题讨论】:

  • 您可以使用(m_key as IDisposable).Dispose(); 进行速记。

标签: c# interface


【解决方案1】:

这称为显式接口实现。在您的示例中,由于您将 Dispose() 方法定义为“void IDisposable.Dispose()”,因此您也在显式实现 IDisposable 接口。

这通常是为了避免冲突。如果 Microsoft 想要添加另一个对 RegistryKey 执行其他操作的 Dispose() 方法,除非他们使用该接口的显式实现,否则他们将无法做到。

这通常使用通用的 IEnumerable 接口来完成。它还要求您实现非泛型接口 IEnumerable。这两个接口中唯一的成员是GetEnumerator,泛型的更有用,所以它通常是这样实现的:

public clas SomeClass : IEnumerable<SomeOtherClass>
{
    public IEnumerator<SomeOtherClass> GetEnumerator ()
    {
        ...
    }

    IEnumerator IEnumerable.GetEnumerator ()
    {
        return GetEnumerator ();
    }
}

这样,当您调用 SomeClass 的 GetEnumator 方法的对象时,它会调用泛型版本,因为另一个是显式实现的,允许我们获得强类型泛型允许。

请参阅 Jesse Liberty 的 Programming C# 的第 166-169 页(我有第四版)。

【讨论】:

    【解决方案2】:

    大多数人不同意我的观点,但我喜欢为所有接口使用显式接口实现。我想弄清楚我是在编写要在我的对象还是在我的界面上调用的方法。

    如果你有一个对象的引用并且想要调用一个接口方法(就像上面的例子),这会很痛苦,但是我通过写来缓解它:

    class C : IDisposable
    {
        public IDisposable IDisposable { get { return this; } }
        void IDisposable.Dispose() { }
    }
    

    这意味着在 C 上调用方法看起来像:

    C c = ...
    c.IDisposable.Dispose();
    

    编译器将其解析为“在 C 上调用 IDisposable 属性,然后在结果上调用 Dispose() 方法”,但我将其读作“在 C 上调用 IDisposable.Dispose() 方法”,这在这里看起来很自然.

    不幸的是,这种方法在使用泛型接口时会变得很丑陋。

    【讨论】:

    • 在显式接口上相同。泛型有时会变得非常困难。
    • Eek - 使用显式接口实现不仅会使调用者在没有上述解决方法的情况下变得更加困难,而且还会破坏继承 - 如果派生类想要覆盖接口方法,则会遇到棘手的情况.我尽可能避免它。
    • 此建议构成不良编程习惯。一个类实现了一个接口,因为它该接口的一个实例。作者关于需要显式转换的论点同样适用于从父类继承的所有方法——除非您转换为父类类型,否则您不应该调用父方法。那会很奇怪——继承的重点是这个类就是那个父类。父类和接口之间的差异是避免继承树中出现菱形的技术差异,而不是语义差异。不要升级它!
    • @Qudeid:在尝试覆盖子类中的实现时会变得很痛苦,一方面(如前所述)。虽然您经常希望将调用者与特定实现分离,但有时他们真的确实想要那个实现,有充分的理由……在这一点上,您让他们的生活变得更加艰难。 “从接口中删除方法”这一点很有趣,但鉴于您必须首先删除接口方法的所有调用者,因此删除实现也不是那么工作。大多数时候我会坚持隐式实现。
    • @JonSkeet 这就是我所做的,但考虑到在我的新工作中看到过度使用显式实现,这真的让我感到震惊,即使是在将其转换为接口以使用成员的情况下,我环顾四周互联网上的更多信息,发现代码项目页面宣扬显式实现作为默认参数,例如从接口中删除方法。现在,如果你需要继承,你有一个在显式实现中被调用的受保护的虚拟方法,你又回到了原点,因为编译器没有告诉你任何关于该方法的信息。谢谢!
    猜你喜欢
    • 2011-05-05
    • 2013-11-17
    • 1970-01-01
    • 2011-02-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多