2016-03-12 11 views
7

多くの型パラメータを使用してインスタンス化される危険性のあるジェネリック型を実装する場合、JITパフォーマンス/コードサイズなどの理由で多くのネストされた非ジェネリック型を避ける必要がありますか?ネストされた型はジェネリック型ではいけませんか?

例:

public class MyGenericType<TKey, TValue> 
{ 
    private struct IndexThing 
    { 
     int row; int col; 
    } 

    private struct SomeOtherHelper 
    { 
     .. 
    } 

    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { } 

} 

同様に動作外の非ジェネリック型を持つことですが、その後、彼らは名前空間を汚染代替。 ベストプラクティスはありますか?

public class MyGenericType<TKey, TValue> 
{ 
    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { } 
} 

internal struct IndexThingForMyGenericType 
{ 
    int row; int col; 
} 

internal struct SomeOtherHelper 
{ 
    ... 
} 
+0

常にボトルネックを起こさずに最適化することはありません。最も理にかなった方法でコードを書いてください。ほとんど問題はありません。質問に関して、私は知っていませんが、BCLで多くのネストされたプライベートクラスを見ることができるので、デザインの観点から、そのルートに行く気はありません。あなたが言う2番目のものは混乱する可能性があります – nawfal

+0

このコードは意味論的に同じではありません。あなたの最初の例では、 'SomeOtherHelper'は一般的なパラメータに依存するため、単一の型ではありません。たとえば、 'typeof(MyGenericType .SomeOtherHelper)'は 'typeof(MyGenericType .SomeOtherHelper)'と同じではありません。 – Enigmativity

+0

名前空間がクリーンなので、クラスをネームスペースに入れることに問題がある場合は、ネームスペースがMyNamespaceの場合はサブニードスペースに追加し、次にMyNamespace.AccesoryNamespaceに追加する場合はサブネームスペースに追加します。 – Gusman

答えて

6

C#では、ネストされたタイプのジェネリックタイプはすべて本質的に汎用です。コンパイラはネストされた型をgenericとしても作成します(私たちの知識はありません)。詳細はthis articleを参照してください。

ジェネリックスはthis interviewで説明されているように参照型のJITコードを共有しますが、ジェネリックスは非ジェネリッククラスと比較してオーバーヘッドがあります。各値の型は独自のJITコードを取得します。

  • タイプが汎用クラスでのみ使用されている場合 - プライベートネストされたタイプであることが理にかなっています。

  • タイプが他の場所で使用されている場合、理想的にはネストされていないタイプ(内部として)である必要があります。

    あなたのネストされたタイプが、この場合、型パラメータTを使用していない場合は、言っ

、それは一般的なタイプのネストされたタイプである必要がありませんので、それは同様に、一般的なタイプになってきて。

ほとんどの場合、実行時に作成される多くの型が懸念される場合は、汎用型をリファクタリングして、ネストされた型のコンテナ型として機能する汎用ではない基本クラスを持つことができます。ネストされた型をprotectedとして公開します。

public class NonGenericBase 
{ 
    protected struct IndexThing 
    { 
     int row; int col; 
    } 

    protected struct SomeOtherHelper 
    { 
     .. 
    } 
} 

public class MyGenericType<TKey, TValue> : NonGenericBase 
{ 
    private struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>> { } 

} 

このように、同じネストされたタイプを共有しています。ランタイムオーバーヘッドはありません。各型パラメータに個別の型はありません。今度はtypeof(MyGenericType<int, string>.SomeOtherHelper)typeof(MyGenericType<long, bool>.SomeOtherHelper)に等しくなります。

+0

私は非ジェネリックベースクラスが行く方法かもしれないと思います。それは実際に有用でない場合だけ悪いことですが、それはまだ私のジェネリック型として公開されていなければなりません。したがって、私は公共のAPIを汚染しました。( 私はまだこれがしかし、少なくとも悪い解決策。 –

1

これは完全に内部的にそれはすべてのポインタについてですので、参照型用のコードが、共有されていることに注意して、質問に答えていませんが。だから、コードベースを膨らませることについて心配する必要はありません。 this答え(アンダーズ・ヘールスバーグは引用符です)。今

、私たちはその後、やっていることは 種類-などList<int>List<long>List<double>List<float>としての価値であるすべてのタイプのインスタンス化のためである - 我々は実行可能なネイティブコードの一意のコピーを作成します。したがって、List<int>は 独自のコードを取得します。 List<long>は独自のコードを取得します。 List<float>は独自の コードを取得します。 すべての参照型について、 という表現が同じであるため、コードを共有します。それは単なるポインタです。

+0

はい、同じサイズの値型でも共有されないので、 'MyGenericType .IndexThing' afaikです。 'MyGenericType .IndexThing'などを再利用しません.2 +型のパラメータを使用すると、すべての参照インスタンスがコードを共有していてもコンビナトリアル爆発が恐れられます! –

+0

@AndersForsgrenはい、わかっています。だから私は* reference *型のために共有されていると言いました。 – Kapol

関連する問題