59

次の状況から開始します。

public interface ISample
{
}

public class SampleA : ISample
{
   // has some (unmanaged) resources that needs to be disposed
}

public class SampleB : ISample
{
   // has no resources that needs to be disposed
}

クラス SampleA は、リソースを解放するためのインターフェイス IDisposable を実装する必要があります。これは、次の 2 つの方法で解決できます。

1. 必要なインターフェースをクラス SampleA に追加します。

public class SampleA : ISample, IDisposable
{
   // has some (unmanaged) resources that needs to be disposed
}

2. これをインターフェイス ISample に追加し、派生クラスに強制的に実装させます。

public interface ISample : IDisposable
{
}

インターフェイスに配置すると、破棄するものがない場合でも、実装に IDisposable の実装を強制します。一方、インターフェイスの具体的な実装には dispose/using ブロックが必要であり、クリーンアップのために IDisposable としてキャストする必要がないことは明らかです。両方の方法でさらにいくつかの長所/短所があるかもしれません...一方の方法を他方よりも優先して使用することをお勧めするのはなぜですか?

4

7 に答える 7

25

IDisposable をインターフェースに追加する場合は、SOLID のインターフェース分離原則に従って、関心のないクライアントにメソッドを提供するため、A に追加する必要があります

それとは別に、使い捨て可能性はインターフェースの具体的な実装に関連するものであり、インターフェース自体には決して関連しないため、インターフェースは決して使い捨てではありません。

破棄する必要がある要素の有無にかかわらず、任意のインターフェイスを潜在的に実装できます。

于 2014-10-17T09:31:56.833 に答える
14

using(){}このパターンをすべてのインターフェースに適用する場合は、ISample派生元にするのが最善です。IDisposableこれは、インターフェースを設計する際の経験則では、「実装の容易さよりも「使いやすさ」を優先するためです。

于 2012-05-09T10:58:29.353 に答える
7

個人的には、すべてISampleの を使い捨てにする必要がある場合は、インターフェースに配置します。一部だけであれば、それがあるべきクラスにのみ配置します。

後者のケースがあるようですね。

于 2012-05-09T10:10:28.103 に答える
6

少なくともいくつかの実装が を実装する可能性があり、少なくともいくつかの場合にインスタンスへの最後に生き残った参照が型の変数またはフィールドに格納される可能性がある場合、インターフェイスIFooはおそらく実装する必要があります。実装が実装される可能性があり、ファクトリ インターフェイスを介してインスタンスが作成される場合は、ほぼ確実に実装する必要があります (多くの場合、ファクトリ インターフェイスを介して作成される のインスタンスの場合と同様)。IDisposableIDisposableIFooIDisposableIDisposableIEnumerator<T>IEnumerable<T>

との比較は参考IEnumerable<T>になりIEnumerator<T>ます。を実装する一部の型はIEnumerable<T>も実装IDisposableしますが、そのような型のインスタンスを作成するコードは、それらが何であるかを認識し、破棄が必要であることを認識し、それらを特定の型として使用します。そのようなインスタンスは、 type として他のルーチンに渡される可能性がありIEnumerable<T>、それらの他のルーチンは、オブジェクトが最終的に破棄する必要があることを知りませんが、ほとんどの場合、それらの他のルーチンはオブジェクトへの参照を保持する最後のルーチンではありません。対照的に、 のインスタンスはIEnumerator<T>、 によって返されるという事実以外に、それらのインスタンスの基になる型について何も知らないコードによって作成、使用、および最終的に破棄されることがよくありますIEnumerable<T>。のいくつかの実装IEnumerable<T>.GetEnumerator()return の実装は、メソッドが破棄される前に呼び出されないとIEnumerator<T>リソースをリークします。型のパラメーターを受け入れるほとんどのコードは、そのような型が渡される可能性があるかどうかを知る方法がありません。返されたオブジェクトを破棄する必要があるかどうかを示すプロパティを含めることや、返されたオブジェクトの型をチェックして実装されているかどうかを確認するルーチンを単に要求することは可能ですが、無条件にメソッドを呼び出す方が迅速かつ簡単です。が必要かどうかを判断し、必要な場合にのみ呼び出します。IDisposable.DisposeIEnumerable<T>IEnumerable<T>EnumeratorTypeNeedsDisposalIEnumerator<T>GetEnumerator()IDisposableDisposeDispose

于 2012-05-09T15:17:02.100 に答える
4

IDispoable は非常に一般的なインターフェイスであるため、インターフェイスがそれを継承していても害はありません。そのため、一部の ISample 実装でノーオペレーション実装を行うという唯一のコストで、コードの型チェックを回避できます。したがって、この観点からは、2 番目の選択肢の方が優れている可能性があります。

于 2012-05-09T10:53:13.177 に答える
1

個人的には 1 を選びますが、具体的な例を 2 つ挙げてください。2 つの良い例はIList.

AnIListは、コレクションのインデクサーを実装する必要があることを意味します。ただし、 はIList実際には であることも意味し、クラスにIEnumerableは が必要です。GetEnumerator()

あなたの場合、実装するクラスが を実装する必要があることをためらっています。インターフェイスを実装するすべてのクラスが実装ISampleする必要があるわけではない場合は、強制しないでください。IDisposableIDisposable

IDispoable特に、特に注目するとIDispoable、クラスを使用するプログラマーはかなり醜いコードを書く必要があります。例えば、

foreach(item in IEnumerable<ISample> items)
{
    try
    {
        // Do stuff with item
    }
    finally
    {
        IDisposable amIDisposable = item as IDisposable;
        if(amIDisposable != null)
            amIDisposable.Dispose();  
    }
}

Dispose()コードがひどいだけでなく、実装で返されるだけであっても、そのリストの反復ごとに項目を破棄するための finally ブロックがあることを確認すると、パフォーマンスが大幅に低下します。

ここのコメントの 1 つに答えるコードを貼り付けて、読みやすくしました。

于 2012-05-09T10:10:55.787 に答える