C# 中类的显式接口实现
本文关键字:显式接口实现 | 更新日期: 2023-09-27 18:00:49
是否有与显式接口实现相媲美的东西,但在 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
类中完成,则由于 new
关键字,覆盖将引用UserClass.B()
;甚至不允许在UserClass
本身中覆盖B
,因为它已经定义了具有该签名的公共方法。
如果在派生的 UserClass
类中有一种方式指定覆盖引用LibraryClass.B()
据我所知是不可能的 - 或者 - 如果B()
可以在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();
}
...
}
除了重命名 UserClass
中的原始B()
之外,语言中是否有任何方法可以解决这种情况(如果它是 Z 公司使用的库本身的一部分,这甚至是不可能的(?如果不是,这是 C# 限制还是 CLR 的限制?
很抱歉这篇文章很长,感谢您阅读到这一点。
编辑:这不是 CLR 限制。C++/CLI 支持解决这种情况的命名覆盖,因此您可以执行类似 virtual void LibraryB(void) = LibraryClass::B;
的操作。C# 设计团队可能只是错过了这个问题。
除了... 语言中是否有任何方法可以解决这种情况,除了...
不,没有。如果您真的觉得这是一种风险,也许可以使用基于接口的设计而不是继承。就个人而言,我觉得这不太可能给您带来任何重大问题,特别是如果您使用比 B()
更具体的方法名称。
LibraryClass
中的新方法B
可用于派生自UserClass
的类,则可以用UserClass
编写如下方法:
public virtual void BNew()
{
return (this as LibraryClass).B();
}
您不能将确切的行为作为显式接口实现。您可以获得的最接近的方法是使用方法隐藏,使用 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
只要我们谈论将 UserClass 中的方法签名更改为"新",我们就可以谈论更改方法名称。所以我在这里没有看到什么大问题,只需在 VS 中单击自动重命名即可。如果自动重命名还不够,因为您在解决方案中以外的其他程序集中使用这些类,则设计(即接口(中可能缺少某些内容。
这个问题是:
- 非常罕见
- 易于解决
如果不是,则:
- 你的设计很糟糕
都应该受到指责:X 创建了一个不是为继承和扩展此类而设计的可继承类,而 Y 创建了一个派生自此类的类。 X 和 Y 都无法预测对方将来将如何扩展各自的代码。 从长远来看,使用接口(在 X 方面(或使用包装类(在 Y 方面(会不那么痛苦。