ジェネリック乱用とは何ですか?


35

いくつかのコードをレビューしているときに、ジェネリックを使用するように変更する機会に気付きました。(難読化された)コードは次のようになります。

public void DoAllTheThings(Type typeOfTarget, object[] possibleTargets)
{
    var someProperty = typeOfTarget.GetProperty(possibleTargets[0]);
    ...
}

このコードは、次のようにジェネリックに置き換えることができます。

public void DoAllTheThings<T>(object[] possibleTargets[0])
{
    var someProperty = type(T).getProperty(possibleTargets[0]);
    ...
}

このアプローチの利点と欠点を研究する際に、ジェネリック乱用と呼ばれる用語を見つけました。見る:

私の質問は2つの部分に分かれています。

  1. このようなジェネリックに移行する利点はありますか?(パフォーマンス?可読性?)
  2. ジェネリック乱用とは何ですか?そして、パラメータの種類があり、一般的なすべての時間を使用している乱用は

2
よくわかりません。オブジェクトのタイプではなく、プロパティの名前を事前に知っているので、リフレクションを使用して値を取得しますか?
メタファイト

3
@MetaFight複雑な例を知っていますが、実際のコードはここに置くのが長くて難しいでしょう。とにかく、私の質問の目的は、Typeパラメーターをジェネリックに置き換えることが良いか悪いかを判断することです。
SyntaxRules

12
私はそれがジェネリックが存在する典型的な理由であると常に考えてきました(おいしいしゃれは意図していません)。あなたのタイプシステムを活用してあなたのためにそれをすることができるのに、なぜ自分でタイプトリックをするのですか?
メタファイト

2
あなたの例では奇妙ですが、私はそれがIODライブラリの多くは何をすべきか正確に感じている
ユアン・

14
答えではありませんが、2番目の例は最初の例よりも柔軟性が低いことに注意してください。関数をジェネリックにすると、ランタイム型を渡すことができなくなります。ジェネリックはコンパイル時に型を知る必要があります。
KChaloux

回答:


87

ジェネリックが適切に適用されると、コードを再配置する代わりに削除します。主に、ジェネリックが削除するのに最適なコードは、タイプキャスト、リフレクション、および動的型付けです。したがって、ジェネリックの乱用は、非ジェネリック実装と比較して、型キャスト、リフレクション、または動的型付けを大幅に削減することなく、ジェネリックコードを作成するものとして大まかに定義できます。

あなたの例に関しては、私は、ジェネリック医薬品の適切な使用を変更することが期待されるobject[]T[]または類似した、と避けるTypetype、完全に。他の場所で大幅なリファクタリングが必要になる場合がありますが、この場合にジェネリックを使用するのが適切な場合は、完了時に全体的にシンプルになります。


3
それは素晴らしい点です。これを読むのが難しいリフレクションを実行していると考えさせられたコードを認めます。に変更できるかどうかを確認object[]T[]ます。
構文規則

3
削除と再配置の特性評価が好きです。
copper.hat

13
ジェネリックの重要な使用法の1つを逃しました-重複を減らすことです。(あなたが言及した他の罪のいずれかを導入することで重複を減らすことができますが)。型キャスト、リフレクション、または動的型付けIMOを削除せずに重複を減らす場合は問題ありません。
マイケルアンダーソン

4
素晴らしい答え。この特定のケースでは、OP はジェネリックではなくインターフェイスに切り替える必要があるかもしれません。オブジェクトの特定のプロパティにアクセスするためにリフレクションが使用されているように見えます。インターフェイスは、そのようなプロパティが実際に存在することをコンパイラが判断できるようにするのにはるかに適しています。
-jpmc26

4
@MichaelAnderson「コードを単に再配置するのではなく削除する」には、重複の削減が含まれます
-Caleth

5

ナンセンスなルールを使用します。ジェネリックは、他のすべてのプログラミング構造と同様に、問題を解決するため存在します。ジェネリックの解決に問題がない場合、それらを使用することは乱用です。

ジェネリックの特定のケースでは、それらは主に具体的なタイプから抽象化するために存在し、異なるタイプのコード実装を1つのジェネリックテンプレートに折り畳むことを可能にします(または、言語で呼び出されるものは何でも)。ここで、typeを使用するコードがあるとしますFoo。その型をgenericに置き換えることTもできますが、そのコードを使用するだけでよい場合Fooは、他のコードと一緒に折り畳むことはできません。したがって、解決すべき問題は存在しないため、ジェネリックを追加する間接性は、読みやすさを低下させるのに役立つでしょう。

したがって、ジェネリックを使用することなくコードを作成することをお勧めします(2番目のインスタンス化が必要なため)。そして、そしてそのときだけ、ジェネリックを使用するためにコードをリファクタリングするときです。その時点より前の使用は、私の目での虐待です。


これは少し面白すぎるように思えますので、思い出させてください:
プログラミングには例外のないルールはありません。このルールが含まれています。


興味深い考え。ただし、条件をわずかに言い換えることで解決できます。OPがintを文字列に、またはその逆にマッピングする新しい「コンポーネント」を必要とするとします。共通のニーズを解決し、ジェネリックにすれば将来簡単に再利用できる可能性があることは非常に明白です。この場合は、ジェネリック乱用の定義に該当します。ただし、非常に一般的な根本的なニーズが特定されたら、これを悪用せずに設計にわずかな追加の努力を投資する価値はありませんか?
クリストフ

@Christopheそれは確かに難しい質問です。はい、ルールを無視する方が良い場合があります(プログラミングには例外のないルールはありません!)。ただし、実際には、特定の文字列変換の場合はかなり複雑です。1.符号付き整数型と符号なし整数型を別々に扱う必要があります。2.整数変換とは別に、float変換を扱う必要があります。3.使用可能な最大の整数型の実装をより小さな型に再利用できます。キャストを行うためにジェネリックラッパーをプロアクティブに作成することもできますが、コアにはジェネリックはありません。
cmaster

1
私は、YAGNIのように一般的な(再使用ではなく使用前の)を積極的に書くことに反対します。必要なときに、リファクタリングします。
Neil_UK

この質問を書いたとき、私は「将来可能性のある再利用を節約するために、可能であれば一般的なものとして書く」というスタンスを取り入れました。私はそれが少し極端であることを知っていました。ここであなたの視点を見るのは爽快です。ありがとう!
構文規則

0

コードは奇妙に見えます。しかし、それを呼び出す場合は、型をジェネリックとして指定するほうが見栄えがよくなります。

https://stackoverflow.com/questions/10955579/passing-just-a-type-as-a-parameter-in-c-sharp

個人的には、呼び出しコードがきれいに見えるこれらのゆがみは好きではありません。たとえば、「流fluentな」もの全体と拡張メソッド。

しかし、人気のある支持者がいることを認めなければなりません。マイクロソフトでさえ、例えばそれを統一して使用します

container.RegisterType<IInterface,Concrete>()

0

私には、あなたはここで正しい道を進んでいたように見えますが、仕事を終えていませんでした。

次のステップのためにobject[]パラメーターを変更することを検討しFunc<T,object>[]ましたか?

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.