ジェネリック引数に制約を設定することに問題はありません。一般的な引数を持つことは、「これは何に対しても機能する」ことを意味するのではなく、コードが意味を成す方法が複数あることを意味します。
のような完全に一般的な概念を実際に公開するかもしれませんがList<T>
、一部のコンテキストでのみ意味を持つ概念を公開する可能性があります ( Nullable<T>
null 非許容エンティティに対してのみ意味をなすなど)。
制約は、クラスがどのような状況下で意味を持つかを世界に伝えるために使用するメカニズムであり、その (制約された) 引数を合理的な方法で実際に使用できるようにしますDispose
。IDisposable
これの極端な例は、コンテキストが非常に制約されている場合です。つまり、可能な実装が 2 つしかない場合はどうでしょうか。実際、現在のコードベースにはそのケースがあり、ジェネリックを使用しています。いくつかのデータ ポイントに対して何らかの処理を行う必要がありますが、現在 (および近い将来) は 2 種類のデータ ポイントしかありません。これは、原則として、私が使用するコードです。
interface IDataPoint
{
SomeResultType Process();
}
class FirstKindDataPoint : IDataPoint
{
SomeResultType Process(){...}
};
class SecondKindDataPoint : IDataPoint
{
SomeResultType Process(){...}
};
class DataPointProcessor<T> where T: IDataPoint
{
void AcquireAndProcessDataPoints(){...}
}
この制約されたコンテキストでも、これは理にかなっています。なぜなら、プロセッサが 1 つしかないため、処理するロジックは 1 つだけであり、同期を維持するために 2 つの別個のプロセッサを使用する必要はありません。
このようにして、プロセッサ内にList<T>
and のAction<T>
代わりにList<IDataPoint>
andAction<IDataPoint>
を含めることができますが、これは私のシナリオでは正しくありません。より具体的なデータ型用のプロセッサが必要なためですIDataPoint
。
である限り、何でも処理するプロセッサが必要な場合はIDataPoint
、その汎用性を取り除き、IDataPoint
コード内で単純に使用するのが理にかなっているかもしれません。
さらに、@dasblinkenlight の回答で提起された点は非常に有効です。ジェネリック パラメーターが構造体とクラスの両方である場合、ジェネリックを使用するとボックス化が回避されます。