2018年8月4日のJohnDemetriouの記事「C#8:Default Interface Methods」1からの翻訳で、いくつかのコンテンツが追加されています
C#8より前
今日は、デフォルトのインターフェース方法について説明します。本当に変に聞こえますね。インターフェイスは、コントラクトを定義するためにのみ使用されます。インターフェイスの実装クラスには一連のパブリックメソッドがありますが、実装クラスには、独自の方法で各メソッドを実装する自由が与えられます。これまでのところ、これらのメソッドの1つ以上の実装を提供する必要がある場合は、継承を使用します。
このクラスがすべてのメソッドを実装するのではなく、それらのサブセットのみを実装することを望む場合は、これらのメソッドとクラス自体を抽象化できます(abstract
)。
たとえば、次のように書くことはできません。
interface IExample
{
void Ex1(); // 允许
void Ex2() => Console.WriteLine("IExample.Ex2"); // 不允许(C# 8 以前)
}
次の抽象クラスに置き換える必要があります。
abstract class ExampleBase
{
public abstract void Ex1();
public void Ex2() => Console.WriteLine("ExampleBase.Ex2");
}
幸いなことに、これは私たちのニーズのほとんどを満たすのに十分です。
C#8の後
では、何か変更はありますか?なぜこの新機能を導入する必要があるのですか?私たちは何を見逃し、私たちが見逃したことに気づかなかったのですか?
ダイヤモンド問題
以下のためにダイヤモンドの問題2、C#(および他の多くの言語)は、多重継承をサポートしていません。ダイヤモンドの問題を回避しながら多重継承を許可するために、C#8ではデフォルトのインターフェイスメソッドが導入されました。
C#8以降では、デフォルトのインターフェイスメソッドを使用して、インターフェイス定義と、定義内の一部またはすべてのメソッドのデフォルトの実装を使用できます。
interface IExample
{
void Ex1(); // 允许
void Ex2() => Console.WriteLine("IExample.Ex2"); // 允许
}
したがって、実装されたメソッドを使用してインターフェイスを実装できるようになり、特定のクラス(共通のメソッドも含まれる)から継承するクラスでのコードの重複を回避できます。
デフォルトのインターフェース方法を使用すると、ダイヤモンド問題は100%解決されていません。これは、クラスが3番目のインターフェイスから継承された2つのインターフェイスから継承し、すべてのインターフェイスが同じメソッドを実装している場合でも発生する可能性があります。
この場合、C#コンパイラは、現在のコンテキストに基づいて適切なメソッドを呼び出すことを選択します。特定のものを推測できない場合は、コンパイルエラーが表示されます。
たとえば、次のインターフェイスがあるとします。
interface IA
{
void DoSomething();
}
interface IB : IA
{
void DoSomething() => Console.WriteLine("I am Interface B");
}
interface IC : IA
{
void DoSomething() => Console.WriteLine("I am Interface C");
}
次に、上記の2つのインターフェイスD
を実装するクラスを作成します。これにより、コンパイルエラーが発生します。
//编译器提示:“D”未实现接口成员“IA.DoSomething()”
public class D : IB, IC
{
}
ただし、メソッドD
の実現の独自のバージョンのクラスの場合DoSomething
、コンパイラは次のメソッド呼び出しを認識します。
public class D : IB, IC
{
public void DoSomething() => Console.WriteLine("I am Class D");
}
Mainメソッドコードが次の場合:
static void Main()
{
var x = new D();
x.DoSomething();
Console.ReadKey();
}
プログラムを実行すると、コンソールウィンドウのI am Class D
出力が次のようになります。
他の利点
メソッドのデフォルトのインターフェース実装を使用すると、APIプロバイダーはレガシーコードの一部を壊すことなく既存のインターフェースを拡張できます。
特性モード
翻訳者注:
コンピュータープログラミングでは、Traitはオブジェクト指向プログラミングで使用される概念であり、クラスの機能を拡張するために使用できる一連のメソッドを表します。3
特性パターンは基本的に、複数のクラスに必要な一連のメソッドです。
これ以前は、C#のTraitパターンは抽象クラスを使用して実装されていました。しかし、多重継承が利用できないため、トレイトパターンの実装は非常にトリッキーになります。そのため、ほとんどの人はそれを回避するか、巨大な継承チェーンで迷子になります。
ただし、インターフェイスでデフォルトのメソッド実装を使用すると、これは変更されます。インターフェイスでデフォルトのインターフェイスメソッドを使用して実装し、クラスが所有する必要のある一連のメソッドを提供してから、これらのクラスにこのインターフェイスを継承させることができます。
もちろん、どのクラスも独自の実装でこれらのメソッドをオーバーライドできますが、そうしたくない場合は、デフォルトの実装のセットを提供します。
以下は翻訳者の補足です
インターフェイスの特定のメソッド
デフォルトのインターフェイスメソッドの最も単純な形式は、インターフェイスで特定のメソッドを宣言することです。これは、本体部分を持つメソッドです。
interface IA
{
void M() {
Console.WriteLine("IA.M"); }
}
このインターフェイスを実装するクラスは、特定のメソッドを実装する必要はありません。
class C : IA {
} // OK
static void Main()
{
IA i = new C();
i.M(); // 输出 "IA.M"
}
クラスC
でのIA.M
最後の置換がであるIA
方法が宣言特定M
。クラスはインターフェースのみを実装でき、インターフェースからメンバーを継承しないことに
注意してください。
C c = new C(); // 或者 var c = new C();
c.M(); // 错误: 类 'C' 不包含 'M' 的定义
ただし、このインターフェイスを実装するクラスが特定のメソッドも実装する場合、その意味は一般的なインターフェイスと同じです。
class C : IA
{
public void M() {
Console.WriteLine("C.M"); }
}
static void Main()
{
IA i = new C();
i.M(); // 输出 "C.M"
}
子インターフェイスは親インターフェイスのメソッドをどのように呼び出しますか?
これは、ブログガーデンの友人がコメントで尋ねた質問です。一見、この質問は非常に興味深いものですが、実際にそのような要求に遭遇することはありますか?考えてみれば、実際に使っているかもしれません。次のようにIB
、親インターフェイスIA
メンバーメソッドを呼び出すインターフェイスで示す簡単な例を次に示しM
ます。
interface IA
{
void M() {
Console.WriteLine("IA.M"); }
}
interface IB : IA
{
//void IA.M() { Console.WriteLine("IB.M"); }
void IB_M() {
M(); }
}
class C : IB {
}
static void Main(string[] args)
{
IB i = new C();
i.IB_M(); // 输出 "IA.M";如果把 IB 中的注释行打开,这里会输出 "IB.M"
}
著者:John Demetriou
翻訳者:Technical Zemin
発行者:Technical Verses
リンク:英語テキスト