web-dev-qa-db-ja.com

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

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からメソッドが非表示になることを指摘しましたが、名前の競合を避ける必要がないのに、なぜwantにするのですか?

11
Nathanus

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

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

12
Chris Pitman

私が見つけた最も有用なアプリケーションは、ファクトリーの実装です。多くの場合、ファクトリ内で変更可能であるが、外部クラスに対して不変であるクラスを作成すると便利です。これは、フィールドとセッターをプライベートにし、ゲッターをパブリックメンバーとして公開するだけで、内部クラスを使用して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;
    }
}

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

4
Elias

クラスが同じシグネチャを持つメンバーを含む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つ(または両方)を明示的に実装すると、それぞれに異なる動作を指定できます。

3
Muhammad Tayyeb

明示的なインターフェイスは、オブジェクトに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();
        }
    }
1
lzcd

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

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

0
Malcolm

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

特に方法はわかりません

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

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

0
Peter Smith