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

Multiple inheritance from the same interface in C#

请考虑以下方案:

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();
  }
}

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

BAR!
baz!
BAR!
baz!

Baz 的声明中包含 , IFoo 似乎很重要,否则 void IFoo.DoFoo() 无法编译。

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

更新:

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

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();
  }
}

打印

BAR!
Foo!

“多重继承”的技巧只允许从派生的 class.

中引入显式实现

从我的角度来看,C# 的这一特性具有潜在的危险,因为如果一个人实现了一个接口的方法,那么无论是从接口还是从 class。如果仅显式或仅隐式实现所有内容,情况确实如此。但是如果两种方式都使用,这个假设就被打破了。所以寓意似乎是:

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

隐式实现往往更常见,使用起来也更方便。它们不那么冗长,任何具体类型的使用都会暴露成员的实现。隐式实现不包括在成员名称之前实现的接口的名称,因此编译器会推断出这一点。成员将公开为 public 并且当对象被转换为具体类型时可以访问。

访问此 link 了解更多详情 https://www.pluralsight.com/guides/distinguish-explicit-and-implicit-interface-implementation-csharp

当您从 IFoo 实现 Baz 时,混乱就开始了。因为 Bar 已经实现了 IFoo 而 Baz 是 Bar 的子类。所以,你不需要那样做。

在面向对象编程中,这不是最佳实践,实际上是最差的实践。

如果你想覆盖DoFoo方法,使用下面的代码

public interface IFoo
{
    void DoFoo();
}

public class Bar : IFoo
{
    public virtual void DoFoo()
    {
        // do something
    }
}

public class Baz : Bar
{
    public override void DoFoo()
    {
        // override what you did in Bar class
    }
}

在您的代码中,当您尝试 baz.DoFoo 时,实际上您是在调用 bar.DoFoo.Because,您并没有覆盖它。就是这个问题。

这是显式实现 (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();
  }
}

输出

baz implicit!
baz explicit!
BAR!
baz explicit!