3

2 つの具体的なクラスを持つインターフェイスがあるとします。具体的には を実装する必要がありますIDisposable。1 つのクラスの利益のために実装するようにインターフェイスを修正する必要がありますか、IDisposableそれともインターフェイスのコンシューマーが使い捨て可能性について実行時チェックを実行する必要がありますか?

インターフェースは単純な変更であるため(特に新しいインターフェースの場合)修正する必要があると思いますが、特定の実装に合わせて設計を変更する際にリスコフ違反の可能性があることもわかります(特に他のクラスが必要な場合)サポートされていない例外をスローします)

4

2 に答える 2

1

Mark Seemann の Dependency Injection に関する本を読んでいるときに、その答えを見つけました。IDisposable は実装の詳細のみであるため、インターフェイス上の IDisposable は自動的に漏れやすい抽象化になります。とはいえ、すべてのインターフェイスが抽象化されているわけではないため、インターフェイスに厳密にプログラミングするという名目で、インターフェイスが IDisposable を実装する必要がある場合があります。

インターフェイスよりも具体的な IDisposable を実装する方がはるかに好ましいですが、どちらの場合も、解決策はリソースに対して大まかな抽象化を作成することです。抽象化の各メソッド実装は、リソースを作成および破棄し、同じことを行う負担から消費者を解放します。消費者側のライフタイム管理の複雑さを軽減するため、私はこのアプローチが好きです (特に DI では、これは実際には何もないはずです)。

上記のシナリオで DI を実装するには、おそらくファクトリを注入して、各メソッドが依存関係をアドホックにインスタンス化できるようにする必要があります。

于 2013-05-18T22:27:42.243 に答える
1

フレームワーク自体が何らかの指標である場合、インターフェースを実装することの適切IDisposable性は、インターフェースが定義する契約を満たすために破棄可能性が必要なプロパティであるかどうかによって異なります。IDisposable以下を含む、少数のフレームワーク インターフェイスが を実装しています。

System.Collections.Generic.IEnumerator<T>
System.Deployment.Internal.Isolation.Store
System.Resources.IResourceReader
System.Resources.IResourceWriter
System.Security.Cryptography.ICryptoTransform
System.ComponentModel.IComponent
System.ComponentModel.IContainer

その性質上、これらのインターフェイスは通常、リソースを消費するため、リソースを解放する必要があるコンストラクトを定義します。その意味で、リソースの破棄は、インターフェイスを実装する具体的なクラスの実装の詳細ではなく、実装契約の不可欠な部分と見なすことができます。たとえば、IResourceReader意志はリソースから読み取り、リソースを閉じることは実装契約の必要な部分です。

対照的に、具体的なクラスがIDisposable(別のインターフェイスを介さずに) 直接実装されるフレームワークでは非常に一般的です。フレームワーク クラスの場合、これはリフレクションを通じてクエリできます。

foreach (var v in typeof(/*any type*/)
                      .Assembly.GetTypes()
                      .Where(a => a.IsClass 
                              && typeof(IDisposable).IsAssignableFrom(a)
                              && a.GetInterfaces().Where(
                               i=>i!=typeof(IDisposable)
                       ).All(i=>!typeof(IDisposable).IsAssignableFrom(i))))
{
   foreach (var s in v.GetInterfaces())
       Console.WriteLine(v.FullName + ":" + s.Name);
}

一般に、これらは、インターフェイス コントラクトの実行に付随して、実装にリソースの消費が必要なクラスです。たとえば、and を個別にSystem.Data.SqlClient.SqlDataAdapter実装します。が処分を必要としないことは十分に可能ですが、 の実装にはリソースの消費と解放が必要です。IDbDataAdapterIDisposableIDbDataAdapterSqlDataAdapter

あなたの場合、あなたのインターフェースを実装する2つのクラスがあることを示しています.1つは実装する必要があり、もう1IDisposableつは実装しません。そうでない場合、リソースを破棄する機能は、定義上、インターフェイスの要件を満たすために不可欠ではありません。したがって、インターフェイス自体は実装すべきではありませんIDisposable

ちなみに、Dispose()例外をスローすべきではありません ( CA1065: 予期しない場所で例外を発生させない を参照してください) IDisposable。すべてのリソースが解放される事後条件が満たされます。を投げる必要はありませんNotSupportedException

補遺

考えられる 2 番目の考慮事項は、予想されるインターフェイスの使用法です。たとえば、データベース シナリオでは次のパターンを使用するのが一般的です。

 System.Data.IDbCommand cmd = ...;
 using (var rdr = cmd.ExecuteReader()) // returns IDataReader (IDisposable)
 {
     while (rdr.Read()) {...}
 } // dispose

IDataReaderを実装しない場合IDisposable、同等のコードはかなり複雑になる必要があります。

 System.Data.IDbCommand cmd = ...;
 System.Data.IDataReader rdr;
 try
 {
     rdr = cmd.ExecuteReader();
     while (rdr.Read()) {...};
 } finally {
     if (rdr is IDisposable) ((IDisposable)rdr).Dispose();
 }

このタイプの使用法が一般的であると予想されるIDisposable場合、すべての実装でIDisposable.

于 2013-05-14T03:54:07.893 に答える