C#中类的显式接口实现

时间:2011-07-01 19:29:35

标签: c#

是否有类似于显式接口实现的东西,但对于C#中的类而言?

考虑以下情况:

公司X提供了一个包含类的库,如下所示:

public class LibraryClass
{
    public virtual void A() { }

    public void DoWork()
    {
        // does something
    }
}

Y公司在其中一个产品中使用此库,并继承自LibraryClass

public class UserClass : LibraryClass
{
    public virtual void B() { }
}

到目前为止一切正常。但是有一天X发布了一个新的库版本并向B()添加了一个虚拟方法LibraryClass

public class LibraryClass
{
    public virtual void A() { }
    public virtual void B() { }

    public void DoWork()
    {
        // does something
        // now uses B with certain semantic assumptions
    }
}

现在Y更新了新的库版本。在使用对新版本的引用进行编译时,编译器会发出警告,指出UserClass.B()隐藏了继承的方法LibraryClass.B(),因此应该指定new关键字或覆盖该方法。由于现有方法UserClass.B()与新引入的方法LibraryClass.B()之间存在语义差距,因此Y决定引入new关键字,因为任何现有的UserClass.B()覆盖都可能无法提供DoWork()期望的语义会破坏代码。另一方面,Y想要使用库的新功能,这需要覆盖LibraryClass.B()。现在这是不可能的:如果覆盖将在UserClass的派生类中完成,则由于UserClass.B()关键字,覆盖将引用new;甚至不允许在B中覆盖UserClass,因为它已经定义了具有该签名的公共方法。

如果在UserClass的派生类中有一种方法指定覆盖引用LibraryClass.B()这种情况可以解决,据我所知,这是不可能的 - 或 - 如果{可以在B()中明确覆盖{1}}:

UserClass

除了重命名public class UserClass : LibraryClass { ... // Override this method in order to change the behavior of LibraryClass.B() public virtual void LibraryB() { } private void override LibraryClass.B() { LibraryB(); } ... } 中的原始B()之外,语言中是否有任何方法可以解决这种情况(如果它是公司消耗的库本身的一部分,甚至可能不可能) Z)?如果不是,这是C#限制还是CLR的限制?

对于这篇长篇文章感到抱歉,感谢您阅读这一点。

修改:这不是CLR限制。 C ++ / CLI支持解决这种情况的命名覆盖,因此您可以执行UserClass之类的操作。 C#设计团队可能只是错过了这个问题。

5 个答案:

答案 0 :(得分:4)

  

除了......之外,语言中是否有任何方法可以解决这种情况?

不,没有。如果您真的感觉这是一种风险,也许使用基于接口的设计而不是继承。就个人而言,我觉得这不太可能导致任何重大问题,特别是如果您使用比B()更具体的方法名称。

答案 1 :(得分:1)

如果您希望B中的新方法LibraryClass可用于从UserClass派生的类,您可以在UserClass中编写一个方法,如下所示:

public virtual void BNew()
{
    return (this as LibraryClass).B();
}

答案 2 :(得分:0)

您不能具有显式接口实现的确切行为。 您可以获得的最接近的方法是使用方法隐藏,使用new关键字。

鉴于这些课程,

    public class C1
    {
        public void A()
        {
            Console.WriteLine ("C1 - A");
        }
        public void B()
        {
            Console.WriteLine ("C1 - B");
        }
    }

    public class C2 : C1
    {
        public new void B()
        {
            Console.WriteLine ("C2 - B");
        }
    }

这会给你这样的行为:

        C1 test = new C2 ();
        test.B ();

        C2 test2 = new C2 ();
        test2.B ();

输出:

C1 - B 
C2 - B

答案 3 :(得分:0)

只要我们谈论将UserClass中的方法签名更改为“&new;'我们可以谈谈改变方法名称。所以我不会在这里看到一个大问题,只需点击VS中的自动名称。如果autorename不够,因为您在其他程序集中使用这些类而不在解决方案中,那么您可能在设计中缺少某些东西(即接口)。

这个问题是:

  • 非常罕见
  • 易于解决

如果不是那样:

  • 你的设计不好

答案 4 :(得分:-1)

在某种程度上,X和Y都应该归咎于:X用于创建一个可继承的类,它不是为继承而设计的,用于扩展这样的类,而Y用于创建一个派生自这样一个类的类。 X和Y都不能预测对方将来如何扩展其各自的代码。从长远来看,使用接口(在X的部分)或使用包装类(在Y的部分)可能不那么痛苦。