【问题标题】: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,那么编译器应该说您隐藏了原始声明。

标签: c# interface methods 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 方法实现相同的结果。
猜你喜欢
  • 2016-10-06
  • 1970-01-01
  • 2012-12-26
  • 2016-04-27
  • 2020-04-01
  • 2011-08-18
  • 1970-01-01
  • 2014-06-20
相关资源
最近更新 更多