web-dev-qa-db-ja.com

メソッドを直接実装できるのに、C#のインターフェイスを使用する理由

私はこれが非常に基本的な質問であることを知っていますが、インタビュアーが非常に巧妙な方法で私に尋ねて、私は無力でした:(

インターフェースのマテリアルまたは理論的な定義のみを知っており、私が取り組んだ多くのプロジェクトでそれを実装しました。しかし、私は本当にこれがなぜそしてどのように役立つのか理解していません。

また、インターフェイスの1つのことも理解していません。つまり、たとえば、

conn.Dispose(); in finallyブロック。しかし、クラスがIDisposableインターフェイス(SqlConnection)クラスを実装または継承していることはわかりません。メソッド名を呼び出す方法を知りたいのですが。同様に、Disposeメソッドがどのように機能するかについても理解していません。すべてのインターフェイスメソッドの独自の実装で関数本体を実装する必要があるためです。それでは、インターフェースはどのように契約として受け入れられ、命名されるのでしょうか?これらの質問は今まで私の心の中に転がり続け、率直に言って、私が理解できる方法で私の質問を説明する良いスレッドを見たことはありませんでした。

MSDNはいつものように非常に怖く見え、そこには明確な行はありません(フォーク、高レベルの開発に親切に言い訳、私はすべてのコードまたは記事がそれを見る人の心に届くべきであると強く感じます、したがって多くのように他の人は、MSDNは役に立たないと言う)。

インタビュアーは言った:

彼には5つのメソッドがあり、クラスに直接実装することを喜んでいますが、Abstractクラスまたはインターフェイスを使用する必要がある場合、どちらを選択し、なぜですか?さまざまなブログで私が読んだすべての内容に、抽象クラスとインターフェースの両方の利点と欠点を挙げて答えましたが、彼は確信しておらず、「なぜインターフェース」を一般的に理解しようとしています。一般に「なぜ抽象クラス」なのかは、同じメソッドを一度だけ実装でき、変更しない場合でも同じです。

ネットのどこにも見当たりませんが、インターフェイスとその機能について明確に説明する記事を入手できました。私はまだ多くのプログラマーの一人であり、インターフェースについてはまだ知りません(私が使った理論と方法は知っています)が、それを明確に理解したことに満足していません。

101
Learner

インターフェースは、次のようなものを作成する場合に優れています。

using System;

namespace MyInterfaceExample
{
    public interface IMyLogInterface
    {
        //I want to have a specific method that I'll use in MyLogClass
        void WriteLog();       
    }

    public class MyClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyClass was Logged");
        }
    }

    public class MyOtherClass : IMyLogInterface
    {

        public void WriteLog()
        {
            Console.Write("MyOtherClass was Logged");
            Console.Write("And I Logged it different, than MyClass");
        }
    }

    public class MyLogClass
    {
        //I created a WriteLog method where I can pass as a parameter any object that implements IMyLogInterface.
        public static void WriteLog(IMyLogInterface myLogObject)
        {
            myLogObject.WriteLog(); //So I can use WriteLog here.
        }
    }

    public class MyMainClass
    {
        public void DoSomething()
        {
            MyClass aClass = new MyClass();
            MyOtherClass otherClass = new MyOtherClass();

            MyLogClass.WriteLog(aClass);//MyClass can log, and have his own implementation
            MyLogClass.WriteLog(otherClass); //As MyOtherClass also have his own implementation on how to log.
        }
    }
}

私の例では、私はMyLogClassを作成する開発者であり、他の開発者はクラスを作成し、ログを取りたい場合は、インターフェースIMyLogInterfaceを実装します。 MyLogClassWriteLog()メソッドを使用するために何を実装する必要があるかを尋ねていたようです。彼らがインターフェースで見つける答え。

インターフェイスを使用する理由の1つは、コードの柔軟性が向上するためです。次のような、クラスタイプAccountのオブジェクトをパラメーターとして取るメソッドを取得したとします。

public void DoSomething(Account account) {
  // Do awesome stuff here.
}

これに伴う問題は、メソッドパラメータがアカウントの実装に向けて固定されていることです。これは、他の種類のアカウントを必要としない場合は問題ありません。この例では、代わりにアカウントインターフェイスをパラメーターとして使用します。

public void DoSomething(IAccount account) {
  // Do awesome stuff here.
}

このソリューションは実装に向けて修正されていません。つまり、SuperSavingsAccountまたはExclusiveAccount(両方ともIAccountインターフェイスを実装)を渡し、実装されたアカウントごとに異なる動作を取得できます。

45
user2211290

インターフェースは、実装者が従わなければならない契約です。 抽象クラスは、コントラクトに加えて共有実装を許可します。これは、インターフェイスにはないものです。クラスは複数のインターフェイスを実装および継承できます。クラスは、単一の抽象クラスのみを拡張できます。

なぜインターフェイス

  • デフォルトまたは共有コードの実装がありません
  • データコントラクト(Webサービス、SOA)を共有したい
  • インターフェース実装者ごとに異なる実装があります(IDbCommandには、特定の方法でインターフェースを実装するSqlCommandおよびOracleCommandがあります
  • 多重継承をサポート にしたい。

なぜ抽象的か

38

enter image description here

そのため、この例では、PowerSocketは他のオブジェクトについて他に何も知りません。オブジェクトはすべて、PowerSocketが提供するPowerに依存しているため、IPowerPlugを実装し、そうすることで接続できます。

インターフェースは、オブジェクトが相互に他のことを何も知らなくても一緒に動作するために使用できるコントラクトを提供するため便利です。

21
Brijesh Rana

一言で言えば-多態性

「実装ではなくインターフェイスにプログラムする」場合、同じインターフェイス(タイプ)を共有する異なるオブジェクトをメソッドに引数として注入できます。この方法では、メソッドコードは別のクラスの実装と結合されません。つまり、同じインターフェイスの新しく作成されたオブジェクトを操作するために常に開いています。 (オープン/クローズの原理)

  • 依存性注入を調べて、GOFのデザインパターン-再利用可能なオブジェクト指向ソフトウェアの要素を必ず読んでください。
20

C#にはカモタイピングがありません-特定のメソッドが一連の具象クラスに実装されていることを知っているからといって、そのメソッドの呼び出しに関してそれらをすべて同じように扱うことができるというわけではありません。インターフェースを実装すると、そのインターフェースが定義するものに関して、それを実装するすべてのクラスを同じタイプのものとして扱うことができます。

4
Erix

抽象クラスとインターフェースはどちらもコントラクトです。

契約の考え方は、いくつかの動作を指定することです。実装したと言ったら、契約に同意しました。

インターフェースに対する抽象の選択は次のとおりです。

抽象クラスの非抽象子孫は、コントラクトを実装します。

versus

インターフェイスを実装するクラスは、コントラクトを実装します。

したがって、すべての子孫が実装する必要のある動作を指定し、別のインターフェイスを定義して保存する場合に抽象を使用しますが、この効果的に集約されたコントラクトを満たすものはすべて子孫でなければなりません。

1
Tony Hopkinson

1つの抽象クラスからのみ継承できます。複数のインターフェースから継承できます。これにより、ほとんどの場合に使用するものが決まります。

抽象クラスの利点は、基本実装を使用できることです。ただし、IDisposableの場合、基本クラスは物事を適切にクリーンアップする方法を知らないため、デフォルトの実装は役に立ちません。したがって、インターフェースの方が適しています。

1
Jeow Li Huan

インターフェイスを使用すると、次のことができます。

1)実装の異なるカットを提供する分離されたインターフェイスを作成し、より凝集したインターフェイスを可能にします。

2)インターフェイス間で同じ名前の複数のメソッドを許可します。これは、競合する実装がなく、署名のみがあるためです。

3)実装に依存せずにインターフェースをバージョニングおよびハイブし、契約が満たされていることを確認できます。

4)コードは、コンクリートではなく抽象化に依存することができ、テストモックなどの注入を含むスマートな依存性注入を可能にします。

私が確信している多くの理由があります、これらはほんのいくつかです。

抽象クラスを使用すると、部分的に具体的なベースを使用できます。これはインターフェイスとは異なりますが、テンプレートメソッドパターンを使用して部分的な実装を作成する機能など、独自の品質があります。

1
Jeff Watkins

@ user2211290回答の実際のサンプルとして:

ArrayListの両方にインターフェースIListがあります。以下にstring[]List<string>があり、IListを使用して一般的なテストを行います。

string[] col1 = { "zero", "one", "two", "three", "four"};
List<string> col2 = new List<string>{ "zero", "one", "two", "three"};

static void CheckForDigit(IList collection, string digit)
    {
        Console.Write(collection.Contains(digit));
        Console.Write("----");
        Console.WriteLine(collection.ToString());
    }

static void Main()
{
    CheckForDigit(col1, "one");   //True----System.String[]
    CheckForDigit(col2, "one");   //True----System.Collections.Generic.List`1[System.String]



//Another test:

    CheckForDigit(col1, "four");   //True----System.String[]
    CheckForDigit(col2, "four");   //false----System.Collections.Generic.List`1[System.String]
}
1
Farzin Kanzi

私は、この質問をする際にすでに多くの血がこぼれたと信じており、多くの人は、普通の人間には理解できないロボットのような用語を説明することでこの問題を解決しようとしています。

だからまず。インターフェースと抽象化の理由を学ぶには、それらが何のためにあるのかを学ぶ必要があります。ファクトリークラスを適用するときに、この2つを個人的に学びました。あなたは良いtuturialを見つける このリンク上

さて、私がすでに与えたリンクに基づいて掘り下げましょう。

Vehicleクラスがあり、ユーザーの要件に応じて変更される可能性があります(Truckタンク飛行機など。

public class clsBike:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Bike");
    }
    #endregion
}

そして

public class clsCar:IChoice
{
   #region IChoice Members
    public string Buy()
    {
       return ("You choose Car");
    }
    #endregion
}

両方とも契約IChoiceを持ち、My ClassにはBuyメソッドが必要だと単純に言う

public interface IChoice
{
    string Buy();
}

さて、あなたはそのインターフェースがメソッドBuy()を強制するだけで、継承されたクラスがそれを実装するときに何をすべきかを決定できるようにします。これはインターフェイスの制限であり、純粋なインターフェイスを使用すると、abstactを使用して自動的に実装できるタスクを繰り返すことになります。この例では、各車両の購入には割引があります。

public abstract class Choice
{
    public abstract string Discount { get; }
    public abstract string Type { get; }
    public string Buy()
    {
       return "You buy" + Type + " with " + Discount;
}
public class clsBike: Choice
{
    public abstract string Discount { get { return "10% Discount Off"; } }
    public abstract string Type { get { return "Bike"; } }
}

public class clsCar:Choice
{
    public abstract string Discount { get { return " $15K Less"; } }
    public abstract string Type { get { return "Car"; } }
}

Factoryクラスを使用すると、同じことを実現できますが、abstractを使用すると、基本クラスにBuy()メソッドを実行させることができます。

まとめ:Interfaceコントラクトは継承クラスに実装を行わせ、抽象クラスコントラクトは実装を初期化できます(Inheritクラスでオーバーライドできます)

1
dr.Crow

インターフェイスを使用すると、クラスデザイナーは使用可能なメソッドをエンドユーザーにとって非常に明確にすることができます。また、それらは多型の不可欠な部分です。

0
poy

理論を非常によく知っていると思うので、抽象クラスに対するインターフェイスの定義を投稿しません。SOLIDの原則を知っていると思いますので、実用的にしましょう。

ご存じのとおり、インターフェイスにはコードを含めることができないため、欠点は非常に簡単に理解できます。

コンストラクターを提供するクラスのプロパティを初期化する必要がある場合、または実装の一部を提供する場合、抽象クラスは、それを実行できないインターフェースに対して適切です。

したがって、非常に一般的には、クラスを継承または拡張するコンストラクターまたは任意のコードをクライアントに提供する必要がある場合、インターフェイスよりも抽象クラスを優先する必要があります。

0