メンバーを非表示にすることのみを目的として明示的なインターフェイス実装を使用する理由は何ですか?


11

C#の複雑さを研究しているときに、明示的なインターフェイスの実装に関する興味深い一節に出会いました。

While this syntax is quite helpful when you need to resolve name clashes, you can use explicit interface implementation simply to hide more "advanced" members from the object level.

の使用を許可するobject.method()か、キャストを要求するかの違いは((Interface)object).method()、経験の浅い目には意地悪な難読化のようです。テキストでは、これによりオブジェクトレベルでIntellisenseからメソッドが非表示になることが示されていますが、名前の競合を回避する必要がないのに、なぜそうする必要があるのでしょうか。


回答:


12

インターフェイスがクラスに、クラスの一部として実際には意味のないメソッドを実装するように強制する状況を想像してください。これは、インターフェースが特に大きく、インターフェース分離の原則に違反している場合によく発生します。

このクラスはインターフェースを実装する必要がありますが、あまり目に見えないパブリックインターフェースを意味のないメソッドで汚染する代わりに、明らかに利用可能にしたくないメソッドを明示的に実装できます。クラスの可視性の高いパブリックインターフェイスは、クラスの使用方法であり、インターフェイスを介してクラスにアクセスすると、明示的な実装が存在します。


6
それは私だけですか、それともひどい考えのように聞こえますか?
Kevin Peno

5
@ケビン、どうやって?場合によっては、インターフェースが制御不能になり、使用する必要があります。耐えがたいインターフェースの損傷を軽減することは合理的に思えます。
Chris Pitman、2011

1
+1は、現実の世界が物事の正しいやり方の邪魔になることを何度も指摘している。@Kevinはい、それはひどい考えですが、時には選択の余地がなく、slopで作業する必要があります。それを隠して、本当に適切に実行できない場合は、適切に実行するためのファサードを作成することをお勧めします。
ウェイン・モリーナ

@ウェイン、私はあなたがそのようなシステムを欲する/使う理由を理解しています。地獄、私はおそらくあなたが述べる場合にそれを使うでしょう。私の短いコメントは、これが不適切であることを本当に指摘しているだけだったと思います。
Kevin Peno

1
過度のインターフェース分離は、構成と集約の主要な障害になる可能性があります。たとえば、IEnumerable<T>他の2つの連結として動作する実装を希望しているとします。IEnumerable<T>そのカウントが既知であるか、潜在的に無限であるか、またはどちらでもないかを示すプロパティと、そのカウントを尋ねるメソッドが含まれている場合、複数を集計IEnumerable<T>して連結として動作するクラスは、いくつかのケースで効率的にカウントを報告できますカプセル化されたコレクションはその数を知っていました。
スーパーキャット2014

4

私が見つけた最も有用なアプリケーションは、工場の実装です。多くの場合、ファクトリ内で変更可能であるが、外部クラスに対して不変であるクラスを作成すると便利です。これは、フィールドとそのセッターをプライベートにして、ゲッターをパブリックメンバーとして公開するだけで、内部クラスを使用してJavaで簡単に実装できます。ただし、C#では、同じことを実現するために明示的なインターフェイスを使用する必要がありました。さらに説明します。

Javaでは、内部クラスと外部クラスは互いにプライベートメンバーにアクセスできます。これは、クラスが非常に密接に関連しているため、これは完全に理にかなっています。それらは同じコードファイルにあり、おそらく同じ開発者によって開発されています。つまり、ファクトリによって内部クラスのプライベートフィールドとメソッドに引き続きアクセスして、それらの値を変更できます。ただし、外部クラスは、パブリックゲッターを経由しない限り、これらのフィールドにアクセスできません。

ただし、C#では、外部クラスは内部クラスのプライベートメンバーにアクセスできないため、概念は直接適用できません。外部クラスでプライベートインターフェイスを定義し、内部クラスで明示的に実装することで、回避策として明示的なインターフェイスを使用しました。この方法では、Javaで行われるのと同じ方法で、外部クラスのみがこのインターフェースのメソッドにアクセスできます(ただし、フィールドではなくメソッドである必要があります)。

例:

public class Factory
{
    // factory method to create a hard-coded Mazda Tribute car.
    public static Car CreateCar()
    {
        Car car = new Car();

        // the Factory class can modify the model because it has access to
        // the private ICarSetters interface
        ((ICarSetters)car).model = "Mazda Tribute";

        return car;
    }

    // define a private interface containing the setters.
    private interface ICarSetters
    {
        // define the setter in the private interface
        string model { set; }
    }

    // This is the inner class. It has a member "model" that should not be modified
    // but clients, but should be modified by the factory.
    public class Car: ICarSetters
    {
        // explicitly implement the setter
        string ICarSetters.model { set; }

        // create a public getter
        public string model { get; }
    }
}

class Client
{
    public Client()
    {
        Factory.Car car = Factory.CreateCar();

        // can only read model because only the getter is public
        // and ICarSetters is private to Factory
        string model = car.model;
    }
}

それは私が明示的なインターフェイスを使用するものです。


3

クラスが同じ署名のメンバーを含む2つのインターフェースを実装する場合、そのメンバーをクラスに実装すると、両方のインターフェースがそのメンバーを実装として使用します。次の例では、すべてのPaint呼び出しが同じメソッドを呼び出しています。C#

class Test 
{
    static void Main()

    {
        SampleClass sc = new SampleClass();
        IControl ctrl = (IControl)sc;
        ISurface srfc = (ISurface)sc;

        // The following lines all call the same method.
        sc.Paint();
        ctrl.Paint();
        srfc.Paint();
    }
}


interface IControl
{
    void Paint();
}
interface ISurface
{
    void Paint();
}
class SampleClass : IControl, ISurface
{
    // Both ISurface.Paint and IControl.Paint call this method. 
    public void Paint()
    {
        Console.WriteLine("Paint method in SampleClass");
    }
}

対照的に、それらの1つ(または両方)を明示的に実装すると、それぞれに異なる動作を指定できます。


1

明示的なインターフェイスは、オブジェクトに2つ以上の非常に異なる形式の通信がある場合に便利です。

たとえば、親と通信する必要がある子オブジェクトのコレクションを保持するオブジェクト。

外部の呼び出し元だけがアクセスできるようにするアクションと、子オブジェクトだけがアクセスできるようにするアクションがあります。

明示的なインターフェイスは、この分割を確実にするのに役立ちます。

    static void Main(string[] args)
    {
        var parent = new Parent();
        parent.DoExternalStuff();
    }

    interface IExternal {
        void DoExternalStuff();
    }

    interface IInternal {
        void DoInternalStuff();
    }

    class Parent : IExternal, IInternal
    {
        public Parent()
        {
            var child = new Child(this);
        }

        public void DoExternalStuff()
        {
           // do something exciting here for external callers
        }

        void IInternal.DoInternalStuff()
        {
            // do something exciting here for internal callers
        }
    }

    class Child
    {
        public Child(IInternal parent)
        {
            parent.DoInternalStuff();
        }
    }

0

おそらく、ユーザーに役立つ機能があったとしても、自分が何をしているのかを本当に理解している場合(機能について学ぶためにドキュメントを読むのに十分なほど)が、そうでない場合は問題が発生する可能性があります。

提供するのではなく、なぜそれを行うのか、私にはわからない2番目の「高度な機能のインターフェイス」と言います。


0

コードは通常、書かれている以上に読み取られます。私はIntellisenseを使用してコードをすばやく作成しています。Intellisenseをより良くするためだけに特定の方法でコードを記述することはしません。

特に方法はわかりません

((Interface)object).method()は、object.method()よりも読みやすくなっています。

特定のオブジェクトに多くのメソッドがある場合、それが神オブジェクトであり、実際には複数のより単純なオブジェクトに分割する必要があるかどうか疑問に思うかもしれません。

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