为啥在实现接口方法时允许使用虚拟?
Posted
技术标签:
【中文标题】为啥在实现接口方法时允许使用虚拟?【英文标题】:why virtual is allowed while implementing the interface methods?为什么在实现接口方法时允许使用虚拟? 【发布时间】:2011-05-27 02:34:42 【问题描述】:我对接口有一个特定的查询。默认情况下,接口方法是抽象和虚拟的,因此如果我们实现该接口并在类中给出定义,我们实际上会覆盖该方法,但是当我们在实现类中再次将该方法标记为虚拟时,为什么编译器不考虑我们实际上是在尝试隐藏原始接口虚拟方法。就像我们在基类中有一个虚拟方法,派生类再次将该方法标记为虚拟,在这种情况下编译器会发出警告,表明您正在隐藏基类方法,因此如果您有意使用 new隐藏基类方法。
public interface ITestInterface
void virtualmethod(); // this method is by default virtual.
public class TestInterface :ITestInterface
public virtual void virtualmethod()
// Now compiler should consider that i am actually hiding the interface virtual method.
如果您为接口构建上述代码并在 ILDASM 中打开,您将看到如下代码:
.method public hidebysig newslot abstract virtual
instance void virtualmethod() cil managed
//end of method ITestInterface::virtualmethod
【问题讨论】:
而不是虚拟,假设接口中的方法默认是抽象的,更有意义。 是的,很好,但是一旦编译器将 virtual 附加到方法中并且您在实现该方法时再次放置 virtual,那么编译器应该说您隐藏了原始声明。 【参考方案1】:从接口实现的方法默认不是虚拟的。您只是提供接口定义中定义的合同的实现。通过将方法标记为 virtual
,您允许派生类提供额外的或单独的实现,同时仍遵守定义的约定。
考虑这个例子:
interface IAnimal
string Speak();
class Dog : IAnimal
public string Speak()
return "Bark!";
Dog
类通过提供契约IAnimal
的实现来实现接口。这里没有虚拟方法,也没有覆盖。
现在考虑这个例子:
interface IAnimal
string Speak();
class Dog : IAnimal
public virtual string Speak()
return "Bark!";
class GoldenRetriever : Dog
public override string Speak()
return "I am a golden retriever who says "
+ base.Speak();
现在Dog
类已将Speak
声明为virtual
,这允许派生类提供额外的或新的实现。这不会违反与IAnimal
的约定,因为对Speak
方法的任何调用仍会返回一个字符串。
好的,最后一个例子。请记住,接口不需要实现——它们只需要满足合同。这意味着接口只关心实现类中是否存在具有匹配签名的成员。这意味着我们也可以这样做:
interface IAnimal
string Speak();
abstract class Dog : IAnimal
public abstract string Speak();
class GoldenRetriever : Dog
public override string Speak()
return "I am a golden retriever";
现在请注意,Dog
类根本没有为 Speak
提供任何实现,但已满足合同的要求。
接口也是从一个类继承到另一个类,所以在上面的所有例子中Dog
和GoldenRetriever
都实现了IAnimal
接口。两个类都隐藏 Speak
方法 - 两个类都实现它。
好的,我认为您的困惑可能来自虚拟方法是在接口中定义的,而不是在类中。这是我在上面定义的接口的 IL:
.class private interface abstract auto ansi IAnimal
.method public hidebysig newslot abstract
virtual instance string Speak() cil managed
虽然方法定义为virtual
是正确的,但您还需要注意这里的类型指定为interface
。这纯粹是微软 C# 编译器生成的 MSIL 的一个实现细节——另一个编译器可以轻松生成不同的代码,只要它在语义上提供相同的结果。
这里重要的是:即使该方法在接口中声明为virtual
,但这并不意味着它与在类中声明的virtual
方法相同。
【讨论】:
请再次检查问题,因为我添加了相同的 IL 代码 .method public hidebysig newslot abstract virtual instance void virtualmethod() cil managed //方法 ITestInterface::virtualmethod 结束【参考方案2】:接口不是基类,所以实现方法不会被覆盖。接口只声明方法,接口方法默认不是虚拟的,事实上接口只声明实现该接口的类上可用的方法。
声明不能是虚拟的。
实现可以是也不能是虚拟的,这完全取决于实现者的逻辑。
【讨论】:
接口只是一个带有虚方法的类型,所以行为应该和类一样。 @Mohit - 这并不完全正确。是的,它们只是另一种类型,但它们是一种 interface 类型,这一切都不同。接口和类在 IL 级别是不同的。【参考方案3】:这里在 IL 和 C# 之间混淆了“虚拟”一词。 它们并不完全对应。在 IL 意义上,虚拟意味着通过 VMT(虚拟方法表)“它被称为间接”,这与类覆盖和接口实现的机制大致相同。 在 IL 的意义上,接口成员必须被标记为虚拟的——没有办法。 但是,如果您查看实现,它会被标记为“虚拟最终”。这是您在 C# 中无法实现的。 'Final' 的意思是,它不能被覆盖。它不会成为 C# 中的虚拟,除非您在 C# 中手动将其声明为“虚拟”或“抽象”。
C# 的隐式接口实现(在 VB.NET 或 IL 中不存在)非常强大。它确实附加了实现类的方法,它在 Name-Parameters-ReturnValue(签名,或 IL 措辞中的 SigAndName)中匹配。 这包括为方法使用基类实现。
public interface ITest
double MethodC(double a, double b, double c, double d);
internal class BaseClass
public double MethodC(double a, double b, double c, double d)
return a+b+c+d;
internal class OtherClass : BaseClass , ITest
这实际上工作正常。 但是 C# 在这里做了一个 Trick,因为你使用 BaseClass.MethodC 作为接口实现,它在 BaseClass 中被标记为 final virtual。 是的,BaseClass 的实现方式,取决于 BaseClass 的使用方式。 BaseClass.MethodC 被修改,因为它用于在派生类中实现 ITest.MethodC。这甚至可以跨越文件和项目边界,只要 BaseClass 的源代码在同一个解决方案中。
所以一个项目编译出来的结果,是不一样的,如果你自己编译,或者和其他产品一起在一个大的解决方案中。这很明显。
如果 BaseClass 的源代码不可用,如果你只是链接到一个 DLL 中,那么 C# 将生成一个 Wrapper 来使用 BaseClass 的实现。 它实际上会这样做:
internal class OtherClass : BaseClass , ITest
double ITest.MethodC(double a, double b, double c, double d)
return base.MethodC(a, b, c, d)
这是秘密进行的,但绝对有必要将方法标记为 IL 意义上的虚拟方法。
【讨论】:
一个更正:C# 一直有sealed
,它对子类中的override
方法实现相同的结果。以上是关于为啥在实现接口方法时允许使用虚拟?的主要内容,如果未能解决你的问题,请参考以下文章