显式接口实现不能是虚的
本文关键字:不能 显式接口实现 | 更新日期: 2023-09-27 18:05:55
声明一下,我已经见过这个connect项,但我真的不明白支持它会有什么问题。
假设我有以下代码:
public interface IInterface
{
void Method();
}
public class Base : IInterface
{
virtual void IInterface.Method()
{
throw new NotImplementedException();
}
}
虚拟标识符有什么问题?使用虚修饰符可以使override
表明基类中有不同的实现。我现在可以通过删除虚拟方法并创建派生类使其工作,如下所示:
public class Derived : IInterface
{
void IInterface.Method()
{
throw new NotImplementedException();
}
}
然而,这样我真的没有任何迹象表明我重写了什么。
更新:
根据c#(第20.4.1部分:显式接口成员实现)规范,有两个原因。
- 隐藏某些方法(我正在使用它)。
- 有两个签名相同但返回类型不同的函数(例如对IClonable很有用)。
但是它没有说明为什么不能使这些方法为虚方法。
更新2:
鉴于这些答案,我想我应该在这里重新表述真正的问题。以上两个原因是为什么显式实现接口成为可能的首要原因。为什么把方法设为虚方法会有问题呢?
显式实现接口的方法具有特殊的可见性作用域=除非您将"this"强制转换为目标接口类型,否则您不能从另一个方法访问它。我想这就是为什么不支持虚拟说明符的原因——你不能覆盖不是普通对象接口(私有/受保护/公共)一部分的方法。
这是我的解决方案:
public class Base : IInterface
{
protected virtual void Method()
{
}
void IInterface.Method()
{
this.Method()
}
}
public class Derived : Base
{
protected override void Method()
{
}
}
但是这样我就没有任何迹象表明我重写了什么
嗯,你有,在某种程度上——你有一个事实,那就是它显然是一个显式接口实现。这表明它为在接口上指定的方法调用提供了多态行为…为什么基类是否也实现接口很重要?当您阅读代码时,这对您有什么影响?
对我来说,声明override
的主要好处是确保我确实获得了正确的签名—它与我试图覆盖的内容相匹配。你已经从显式接口实现中得到了好处,就像你给了一个不存在的方法或错误的参数等,编译器已经抱怨了。
我可以排序理解你的观点,但我从来没有发现这是一个实际的问题。
能够让显式接口实现是虚拟的只在一种情况下有用:当派生类重写需要调用父类实现时。不幸的是,即使显式接口实现可以成为虚的,如果没有一些新的语法,重写类也无法调用父类的实现。VB.net很好地处理了这个问题,它允许一个实现接口的方法被声明为Protected
,并使用与接口方法不同的名称。因此,派生类可以覆盖Protected
方法(使用适当的名称),并且该覆盖可以调用父类版本(使用相同的名称)。
如果只有一个接口被继承,为什么你需要这样做:
public class Base : IInterface
{
virtual void IInterface.Method()
{
throw new NotImplementedException();
}
}
为什么不直接去呢:
public class Base : IInterface
{
virtual void Method()
{
throw new NotImplementedException();
}
}
我认为原因可以简单地用下面的例子来说明。考虑以下代码:
public interface IInterfaceA
{
void Method();
}
public interface IInterfaceB
{
void Method();
}
public class Base : IInterfaceA, IInterfaceB
{
virtual void IInterfaceA.Method()
{
...
}
virtual void IInterfaceB.Method()
{
...
}
}
public class Derived : Base
{
public override void Method()
{
// Will this override IInterfaceA or IInterfaceB implementation???
}
}
所以,简而言之,如果你显式地用相同的方法签名实现多个接口,你的派生类将不知道你想要覆盖哪个基方法。