C# 中同一接口的多重继承

apa*_*ara 5 c# multiple-inheritance

请考虑以下程序:

using System;

public interface IFoo
{
    void DoFoo();
}

public class Bar: IFoo
{
    public void DoFoo() => Console.WriteLine("BAR!");
}

public class Baz: Bar, IFoo
{
    void IFoo.DoFoo() => Console.WriteLine("baz!");
}

class Program
{
  static void Main()
  {
    Baz baz = new Baz();
    baz.DoFoo();
    
    IFoo foo = baz;
    foo.DoFoo();
    
    Bar bar = baz;
    bar.DoFoo();
    
    IFoo foobar = bar;
    foobar.DoFoo();
  }
}
Run Code Online (Sandbox Code Playgroud)

它给出了以下输出,我个人以我的 C++ 背景认为这是非常意外的:

BAR!
baz!
BAR!
baz!
Run Code Online (Sandbox Code Playgroud)

, IFoo在声明中包含Baz似乎是实质性的,因为否则void IFoo.DoFoo()无法编译。

有人可以解释一下这里发生了什么(特别是最后一行)吗?在现实生活中应该怎样防止此类行为呢?是否应该完全避免从同一接口实现,或者是否有其他一些规则来避免出现问题?

更新:

看起来这里的主要问题不是“多重继承”(实际上并不是真正的多重继承),而是在 C# 中实现接口方法的方式。也就是说,同一类中的同一方法可以有两种不同的实现,其中一种是显式的,另一种是隐式的。例如这个程序:

using System;

public interface IFoo
{
    void DoFoo();
}

public class Bar: IFoo
{
    void IFoo.DoFoo() => Console.WriteLine("Foo!");
    public void DoFoo() => Console.WriteLine("BAR!");
}

class Program
{
  static void Main()
  {
    Bar baz = new Bar();
    baz.DoFoo();
    
    IFoo foo = baz;
    foo.DoFoo();
  }
}
Run Code Online (Sandbox Code Playgroud)

印刷

BAR!
Foo!
Run Code Online (Sandbox Code Playgroud)

“多重继承”的技巧只允许从派生类引入显式实现。

从我的角度来看,C# 的这一特性具有潜在的危险,因为如果实现接口的方法,通常会期望无论是从接口还是从类调用该方法,都会调用相同的方法。如果一个人仅显式或仅隐式地实现所有内容,情况确实如此。但如果两种方式都使用,这个假设就被打破了。所以道德似乎是:

  1. 如果您不想出于某种目的使用这种奇怪的效果,请不要混合同一方法的隐式和显式实现。
  2. 在派生类中谨慎使用显式实现。

小智 5

void IFoo.DoFoo()这是显式实现 ( ) 与隐式实现 ( )的区别public void DoFoo()。编译器将首先使用显式实现。如果您同时提供显式和隐式实现,那么差异就会变得很明显:

https://dotnetfiddle.net/7l9gIs


using System;

public interface IFoo
{
    void DoFoo();
}

public class Bar: IFoo
{
    public void DoFoo(){ Console.WriteLine("BAR!"); }
}

public class Baz: Bar, IFoo
{
    void IFoo.DoFoo(){ Console.WriteLine("baz explicit!"); }
    public new void DoFoo(){ Console.WriteLine("baz implicit!"); }
}

public class Program
{
  public static void Main()
  {
    Baz baz = new Baz();
    baz.DoFoo();
    
    IFoo foo = baz;
    foo.DoFoo();
    
    Bar bar = baz;
    bar.DoFoo();
    
    IFoo foobar = bar;
    foobar.DoFoo();
  }
}
Run Code Online (Sandbox Code Playgroud)

输出

baz implicit!
baz explicit!
BAR!
baz explicit!
Run Code Online (Sandbox Code Playgroud)