0

如果我有两个类既实现了接口又继承了,我需要使函数虚拟吗?例如给定:

interface IDoSomething
{
    void DoSomething();
}

class A : IDoSomething
{
    public void DoSomething()
    {
        //do A
    }
}

class B : A
{
    public new void DoSomething()
    {
        //do B
    }
}

下面的代码会做A还是B?

IDoSomething doer = new B();
doer.DoSomething(); //do A or do B?

我很困惑,因为我的印象是所有接口调用实际上都是虚拟的,但显然我正在使用 new 运算符来隐藏基本定义。

4

4 回答 4

4

是解释。已经在 stackoverflow 论坛上可用。

在此处通过 CSharp 第 3 版从 CLR 引用 Jeffrey Ritcher

CLR 要求将接口方法标记为虚拟。如果您没有在源代码中明确将方法标记为虚拟,则编译器会将方法标记为虚拟和密封;这可以防止派生类覆盖接口方法。如果显式将该方法标记为虚拟,则编译器将该方法标记为虚拟(并使其未密封);这允许派生类覆盖接口方法。如果接口方法是密封的,则派生类不能覆盖该方法。但是,派生类可以重新继承相同的接口,并且可以为接口的方法提供自己的实现。

于 2012-06-12T11:29:41.987 回答
2
class A : IDoSomething
{
    public virtual void DoSomething()
    {
        //do A
    }
}

class B : A
{
    public override void DoSomething()
    {
        //do B
    }
}
于 2012-06-12T11:28:54.713 回答
0

我更喜欢leppie的解决方案。如果这不是一个选项:

class A : IDoSomething
{
    void IDoSomething.DoSomething()
    {
        //do A
    }
}

class B : A
{
    void IDoSomething.DoSomething()
    {
        //do B
    }
}

但请注意,这将隐藏实现,所以你不能做((A)doer).DoSomething().

如果您不能将 A 类更改为这些解决方案中的任何一个,我认为在所有情况下都没有确定的方法来覆盖它。您既可以显式实现接口,也可以public new在 B 上创建一个方法。这样,如果它被静态称为 anIDoSomething或 a B,它将使用 B 的实现,但如果它被称为 an A,它仍将使用A' 实现。

于 2012-06-12T11:32:53.470 回答
0

尽管 C# 和 .net 允许派生类重新实现接口方法,但通常最好让基类使用虚拟方法来实现接口,并让派生类覆盖该方法,在派生类可能出现的任何情况下希望增加而不是完全替换基类实现。在像 vb.net 这样的一些语言中,这可以直接完成,而不管类是否公开了与正在实现的接口成员具有相同名称和签名的公共成员。在 C# 等其他语言中,实现接口的公共方法可以标记为未密封和虚拟(允许派生类覆盖它并进行覆盖调用base.Member(params),但显式接口实现不能。在这些语言中,最好的方法是像:

类 MyClass : MyInterface
{
  void MyInterface.DoSomething(int param)
  {
    做某事(参数);
  }
  受保护的虚拟 void doSomething(int param)
  {
    ...
  }
}
类 MyClass2 : MyClass
{
  受保护的覆盖无效doSomething(int param)
  {
    ...
    base.doSomething(参数);
    ...
  }
}

在某些情况下,让接口实现包装虚拟调用可能是有利的,因为它允许基类确保某些事情发生在被覆盖的函数之前或之后。例如,Dispose 的非虚拟接口实现可以包装虚拟 Dispose 方法:

  int DisposingFlag; // System.Boolean 不适用于 Interlocked.Exchange
  无效 IDisposable.Dispose()
  {
    if (Threading.Interlocked.CompareExchange(DisposingFlag, 1, 0) == 0)
    {
      处置(真);
      处置标志 = 2;
      Threading.Thread.MemoryBarrier();
      GC.SuppressFinalize(this);
    }
  }
  public bool Disposed { get {return (DisposingFlag != 0);} }
  public boolfullyDisposed { get {return (DisposingFlag > 1);} }

这将(与 Microsoft 的默认包装器不同)确保Dispose只被调用一次,即使多个线程尝试同时调用它。此外,它使Disposed属性可用。使用微软的包装器,每个需要标志的派生类Disposed都必须定义自己的;即使基类Disposed标志是受保护的或公共的,使用它也不安全,因为直到派生类已经开始清理之后才会设置它。包装内的设置DisposingFlag避免了这个问题。

于 2012-06-12T15:12:33.343 回答