为啥在实现接口方法时允许使用虚拟?

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 提供任何实现,但已满足合同的要求。

接口也是从一个类继承到另一个类,所以在上面的所有例子中DogGoldenRetriever都实现了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 方法实现相同的结果。

以上是关于为啥在实现接口方法时允许使用虚拟?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 C# 接口方法没有声明为抽象或虚拟的?

虚拟机 安装版的xp 为啥在进入xp的安全模式时 会蓝屏?

为啥在此代码中调用虚拟方法时会出现分段错误?

为啥 C# 不允许静态方法实现接口?

虚拟机的linux为啥连不上网?

C# 如何在 C++ 不允许虚拟模板方法的情况下允许虚拟泛型方法?