インターフェースを明示的に実装する理由


122

それでは、インターフェイスを明示的に実装するための適切なユースケースは何でしょうか?

それは、クラスを使用している人々がインテリセンスでそれらのメソッド/プロパティをすべて見る必要がないようにするためだけですか?

回答:


146

2つのインターフェースを実装し、どちらも同じメソッドと異なる実装を使用する場合は、明示的に実装する必要があります。

public interface IDoItFast
{
    void Go();
}
public interface IDoItSlow
{
    void Go();
}
public class JustDoIt : IDoItFast, IDoItSlow
{
    void IDoItFast.Go()
    {
    }

    void IDoItSlow.Go()
    {
    }
}

はい、まさにこれがEIMIが解決する1つのケースです。そして、他のポイントは「マイケルB」の回答でカバーされています。
2010年

10
素晴らしい例。インターフェイス/クラス名が大好きです!:-)
ブライアンロジャース

11
私はそれが好きではありません、クラスで同じシグネチャを持つ2つのメソッドが非常に異なることをしますか?これは非常に危険なものであり、大規模な開発では大混乱を引き起こす可能性が非常に高いです。このようなコードがある場合、分析と設計は非常に重要です。
Mick、

4
@Mikeインターフェースは、いくつかのAPIまたは2つの異なるAPIに属している可能性があります。はここでは少し誇張されているかもしれませんが、少なくとも明示的な実装が利用できることを嬉しく思います。
TobiMcNamobi 2015年

@BrianRogersとメソッド名も;-)
–Sнаđошƒа

66

非優先メンバーを非表示にすると便利です。たとえば、両方を実装しIComparable<T>IComparableいて、通常、IComparableオーバーロードを非表示にして、さまざまなタイプのオブジェクトを比較できるという印象を人々に与えないほうがよい場合は、同様に、一部のインターフェイスはのようにCLSに準拠しIConvertibleていません。そのため、インターフェイスを明示的に実装しない場合、CLS準拠を必要とする言語のエンドユーザーはオブジェクトを使用できません。(BCLの実装者がプリミティブのIConvertibleメンバーを隠さなかった場合、これは非常に悲惨なことになります:))

もう1つの興味深いメモは、通常、このような構成を使用すると、明示的にインターフェースを実装する構造体は、インターフェースタイプにボックス化することによってのみ呼び出すことができるということです。これを回避するには、一般的な制約を使用します。

void SomeMethod<T>(T obj) where T:IConvertible

intに渡すときにintをボックス化しません。


1
制約にタイプミスがあります。明確にするために、上記のコードは機能します。これは、インターフェイスのメソッドシグネチャの初期宣言に含まれている必要があります。元の投稿ではこれを指定していませんでした。また、適切な形式は「void SomeMehtod <T>(T obj)where T:IConvertibleです。「)」と「where」の間に余分なコロンがあってはいけないことに注意してください。高価なボクシングを避けるためのジェネリック
Zack Jannsen 2012

1
こんにちはマイケルB.では、なぜ.NETでの文字列の実装にIComparableのパブリック実装があるのか​​。} if(!(value is String)){新しいArgumentException(Environment.GetResourceString( "Arg_MustBeString"));をスローします); } return String.Compare(this、(String)value、StringComparison.CurrentCulture); }ありがとう!
zzfima 2015年

stringジェネリックの前に出回っていましたが、この習慣は流行していました。.net 2が登場したとき、彼らはのパブリックインターフェイスを壊したくなかったstringので、安全策を講じていたのでそのままにしておきました。
マイケルB

37

インターフェースを明示的に実装するいくつかの追加の理由:

下位互換性ICloneableインターフェースが変更された場合でも、メソッドクラスメンバーの実装は、メソッドシグネチャを変更する必要はありません。

よりクリーンなコードCloneメソッドがICloneableから削除されるとコンパイラエラーが発生しますが、メソッドを暗黙的に実装すると、未使用の「孤立した」パブリックメソッドが発生する可能性があります

強い型付け:例を使用してsupercatのストーリーを説明するために、これは私の好ましいサンプルコードです。ICloneable明示的に実装Clone()すると、MyObjectインスタンスメンバーとして直接呼び出すときに強く型付けすることができます。

public class MyObject : ICloneable
{
  public MyObject Clone()
  {
    // my cloning logic;  
  }

  object ICloneable.Clone()
  {
    return this.Clone();
  }
}

そのため、私は私が好むと思いinterface ICloneable<out T> { T Clone(); T self {get;} }ます。ICloneable<T>T には意図的に制約がないことに注意してください。オブジェクトは通常、そのベースが可能な場合にのみ安全に複製できますが、できないクラスのオブジェクトを安全に複製できる基本クラスから派生したい場合があります。それを可能にするために、継承可能なクラスがパブリッククローンメソッドを公開しないようにすることをお勧めします。代わりに、protected複製メソッドを持つ継承可能なクラスと、それらから派生し、公開複製を公開するシールされたクラスがあります。
スーパーキャット2013年

BCLにICloneableの共変バージョンがないことを除けば、それはより良いでしょう。そのため、1つを作成する必要がありますか?
Wiebe Tijsma 2013年

3つの例はすべて、ありそうもない状況に依存しており、インターフェースのベストプラクティスに違反しています。
MickyD 2018

13

別の便利な手法は、関数のメソッドのパブリック実装に、インターフェースで指定された値よりも具体的な値を返すようにすることです。

たとえば、オブジェクトはを実装できますがICloneable、公開されているCloneメソッドは独自の型を返します。

同様に、IAutomobileFactoryManufactureを返すメソッドを持っている可能性がありますAutomobileFordExplorerFactory、を実装IAutomobileFactoryするはそのManufactureメソッドがFordExplorer(から派生するAutomobile)を返す可能性があります。型キャストする必要なしにによって返されたオブジェクトの-use固有のプロパティをFordExplorerFactory使用できることを知っているコードは、何らかのタイプがあることを単に知っているコードは、その戻りをとして処理するだけです。FordExplorerFordExplorerFactoryIAutomobileFactoryAutomobile


2
+1 ...これは、明示的なインターフェイス実装の私の好ましい使用法ですが、小さなコードサンプルはおそらくこのストーリーよりも少し明確です:)
Wiebe Tijsma '16

7

同じメンバー名とシグニチャーを持つ2つのインターフェースがあり、使用方法に応じてその動作を変更したい場合にも役立ちます。(私はこのようなコードを書くことをお勧めしません):

interface Cat
{
    string Name {get;}
}

interface Dog
{
    string Name{get;}
}

public class Animal : Cat, Dog
{
    string Cat.Name
    {
        get
        {
            return "Cat";
        }
    }

    string Dog.Name
    {
        get
        {
            return "Dog";
        }
    }
}
static void Main(string[] args)
{
    Animal animal = new Animal();
    Cat cat = animal; //Note the use of the same instance of Animal. All we are doing is picking which interface implementation we want to use.
    Dog dog = animal;
    Console.WriteLine(cat.Name); //Prints Cat
    Console.WriteLine(dog.Name); //Prints Dog
}

60
私が今まで見た中で最も奇妙なOO関連の例:public class Animal : Cat, Dog
mbx

38
@mbx:アニマルもオウムを実装した場合、ポリーモーフィングアニマルになります。
RenniePet 2013

3
片方が猫でもう片方が犬だった漫画のキャラクターを覚えています;-)
George Birbilis

2
「80年代のテレビシリーズ.. 『Manimal』 ..男はに変身可能性があった...ああ、用事
bkwdesign

モルキーはちょっと猫のように見え、忍者猫のようでもあります。
samis

6

つまり、インターフェースを明示的に実装するためにパブリックインターフェースをよりクリーンに保つことができます。つまり、FileクラスがIDisposable明示的に実装Close()し、コンシューマにとってDispose()よりも意味のあるパブリックメソッドを提供する場合があります。

F#は明示的なインターフェイスの実装のみを提供するため、その機能にアクセスするには常に特定のインターフェイスにキャストする必要があります。これにより、インターフェイスが非常に明示的に(しゃれがなく)使用されます。


VBのほとんどのバージョンでも、明示的なインターフェイス定義のみがサポートされていると思います。
Gabe

1
@Gabe-VBの場合よりも微妙です。インターフェイスを実装するメンバーの命名とアクセス可能性は、それらが実装の一部であることを示すこととは別です。したがって、VBでは、@ Iainの回答(現在のトップの回答)を見ると、IDoItFastとIDoItSlowをそれぞれパブリックメンバー "GoFast"と "GoSlow"で実装できます。
Damien_The_Unbeliever 2010年

2
私はあなたの特定の例が好きではありません(IMHO、非表示Disposeにする必要があるのは、クリーンアップを必要としないものだけです)。より良い例は、不変コレクションのの実装のようなものですIList<T>.Add
スーパーキャット2013年

5

内部インターフェイスがあり、クラスのメンバーをパブリックに実装したくない場合は、それらを明示的に実装します。暗黙的な実装は公開する必要があります。


わかりました、それはプロジェクトが暗黙的な実装でコンパイルされない理由を説明しています。
pauloya 2011

4

明示的な実装のもう1つの理由は、保守性です。です。

クラスが "ビジー"になった場合(そうなったとしても、他のチームメンバーのコードをリファクタリングする贅沢さはありません)、明示的な実装があると、インターフェイスコントラクトを満たすためのメソッドがそこにあることが明らかになります。

したがって、コードの「読みやすさ」が向上します。


私見では、クラスがメソッドをクライアントに公開するかどうかを決定することがより重要です。それが明示的であるか暗黙的であるかを決定づけます。複数のメソッドが一緒に属していることを文書化するために、この場合、それらは契約を満たしているため- #region適切なタイトル文字列を使用して、それが目的です。そして、メソッドに関するコメント。
ToolmakerSteve 2017年

1

別の例を以下に示します。 System.Collections.Immutable、作成者がこの手法を使用して、コレクション型の使い慣れたAPIを保持しながら、新しい型に意味のないインターフェースの部分を削り取りました。

具体的には、ImmutableList<T>器具IList<T>したがってICollection<T>順番に許可するImmutableList<T>レガシーコードとより容易に使用される)、まだvoid ICollection<T>.Add(T item)ため意味をなさないImmutableList<T>:不変のリストに要素を追加するので、既存のリストを変更してはならない、ImmutableList<T>また、由来しIImmutableList<T>、そのIImmutableList<T> Add(T item)ために使用することができます不変のリスト。

したがって、の場合Add、実装はImmutableList<T>次のようになります。

public ImmutableList<T> Add(T item)
{
    // Create a new list with the added item
}

IImmutableList<T> IImmutableList<T>.Add(T value) => this.Add(value);

void ICollection<T>.Add(T item) => throw new NotSupportedException();

int IList.Add(object value) => throw new NotSupportedException();

0

明示的に定義されたインターフェースの場合、すべてのメソッドは自動的にプライベートになり、アクセス修飾子をパブリックに与えることはできません。と仮定します:

interface Iphone{

   void Money();

}

interface Ipen{

   void Price();
}


class Demo : Iphone, Ipen{

  void Iphone.Money(){    //it is private you can't give public               

      Console.WriteLine("You have no money");
  }

  void Ipen.Price(){    //it is private you can't give public

      Console.WriteLine("You have to paid 3$");
  }

}


// So you have to cast to call the method


    class Program
    {
        static void Main(string[] args)
        {
            Demo d = new Demo();

            Iphone i1 = (Iphone)d;

            i1.Money();

            ((Ipen)i1).Price();

            Console.ReadKey();
        }
    }

  // You can't call methods by direct class object

1
「すべてのメソッドは自動的にプライベートです」-これは技術的には正しくありません。実際にプライベートだった場合、キャストしたかどうかに関係なく、呼び出し不可能になります。
samis

0

これは、明示的なインターフェイスを作成する方法です 。2つのインターフェイスがあり、両方のインターフェイスに同じメソッドがあり、単一のクラスがこれらの2つのインターフェイスを継承している場合、1つのインターフェイスメソッドを呼び出すと、コンパイラーはどのメソッドを呼び出すか混乱しました。明示的インターフェースを使用してこの問題を管理します。ここに私が下に挙げた一例があります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace oops3
{
    interface I5
    {
        void getdata();    
    }
    interface I6
    {
        void getdata();    
    }

    class MyClass:I5,I6
    {
        void I5.getdata()
        {
           Console.WriteLine("I5 getdata called");
        }
        void I6.getdata()
        {
            Console.WriteLine("I6 getdata called");
        }
        static void Main(string[] args)
        {
            MyClass obj = new MyClass();
            ((I5)obj).getdata();                     

            Console.ReadLine();    
        }
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.