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。如果仅显式或仅隐式实现所有内容,情况确实如此。但是如果两种方式都使用,这个假设就被打破了。所以寓意似乎是:
- 如果您不打算出于某种目的使用这种奇怪的效果,请不要混用同一方法的隐式和显式实现。
- 在派生的 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!
请考虑以下方案:
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。如果仅显式或仅隐式实现所有内容,情况确实如此。但是如果两种方式都使用,这个假设就被打破了。所以寓意似乎是:
- 如果您不打算出于某种目的使用这种奇怪的效果,请不要混用同一方法的隐式和显式实现。
- 在派生的 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!