2011-12-29 7 views
3

C#での制約、そして自分自身に次のような課題を思い付いた:ユーザー定義のコンパイル時の型私は現在、C#でジェネリックを試してい

汎用関数f<T>考えると、Tはコンパイル時に検証与えられた集合[T1、T2、...、Tn]からの型。 f<T>に我々は

CompileTimeAssert<T>.isContainedIn<TypeList<string, int, bool>>(); 

を持っている場合たとえば、その後、f<int>はコンパイルしなければならない、とf<double>はコンパイルべきではありません。

まだありません。上記のコードを考える

interface ContainsType<T> {} 

class TypeList<T1>: ContainsType<T1> {} 
class TypeList<T1, T2>: TypeList<T2>, ContainsType<T1> {} 
class TypeList<T1, T2, T3>: TypeList<T2, T3>, ContainsType<T1> {} 
class TypeList<T1, T2, T3, T4>: TypeList<T2, T3, T4>, ContainsType<T1> {} 
// add longer type lists to taste 

class CompileTimeAssert<T> 
{ 
    public static void isContainedIn<TypeList>() 
     where TypeList: ContainsType<T> {} 
    public static void isContainedIn<TypeList>(TypeList tl) 
     where TypeList: ContainsType<T> {} 
} 

は、(予想通り)、次のコンパイル:

// uses first overload 
CompileTimeAssert<int>.isContainedIn<TypeList<string, int, bool>>(); 

var myTypeList = new TypeList<string, bool>(); 
CompileTimeAssert<string>.isContainedIn(myTypeList); // uses second overload 

そして次はコンパイルされません、また期待通り:これは私がこれまで持っているものである

CompileTimeAssert<short>.isContainedIn<TypeList<string, int, bool>>(); 

var myTypeList = new TypeList<string, bool>(); 
CompileTimeAssert<double>.isContainedIn(myTypeList); 

これはとてもかわいいですが、役に立たないこともあります。

void f<T>() 
{ 
    CompileTimeAssert<T>.isContainedIn<TypeList<string, int, bool>>(); 
} 

をしてからf<int>コンパイルし、コンパイル・エラーでf<double>結果を持っている:1は、次の操作を行うことができれば、それははるかに便利になります。

上記のように、f<T>はコンパイルに失敗します(具体的な種類の呼び出しに関係なく)。 私がする(Mac OS X上でMonoDevelopのを使用して)、次のエラーを取得しています:

エラーCS0311: ' へ 'タイプTypeList<string,int,bool>' cannot be used as type parameter 'TypeList' in the generic type or method 'CompileTimeAssert<T>.isContainedIn<TypeList>()'. There is no implicit reference conversion fromタイプリストContainsType'

を、これはdoesnの理由を私は一種の理解'今のところ、私は働いている代替案を考え出すことができませんでした。誰も私が望むものがC#で可能かどうかについてのアイデアはありますか?

ありがとうございました。

+0

あなたはおそらく、アスペクト指向プログラミングを使用してこの機能を実装する方がよいでしょう。 http://www.sharpcrafters.com/ –

+0

あなたは本当にあなたのインタフェースの前に 'I'を付けてください。 – bevacqua

+0

M.Babcock:ありがとう、PostSharpは涼しくて有望で、私はそれを "チェックアウト"リストに入れます。私がC#を習得したばかりなので、それが始まるまでには時間がかかるかもしれません。 –

答えて

2

.NETのジェネリックはコンパイル時の構造と同じくらいランタイム構造であるため、難題の目標は無駄です。あなたのプログラムがエラーなしでコンパイルされたとしても、 "承認されていない"タイプを渡すことで、あなたのジェネリックをリフレクションを通して拡張することができます。

私はAndrei Alexandrescuの本をとても愛していましたが、C#のジェネリックスを理解する上で重要なことは、のジェネリックではないということです。小さな構文上の類似点を除けば、「同じ野球場ではなく、同じリーグではなく、同じスポーツでもない」というようなものではない。ソートのコンパイル時検証の必要性の背後にある大きな原動力は、 "intはOKです、倍精度はOKではありませんでした)暗黙的にアクセス操作:a+bは、テンプレート拡張でabの型+の操作が許可されています。C#では同じではありません。ジェネリックに渡される値に対して操作を実行する場合は、その操作の存在を型制約で明示的に指定するか、その操作を実行する(デリゲート、インターフェイスなど)いずれにせよ、あなたのテンプレートがint型のナットのためにdouble型ではないことをコンパイラに伝えても、あなたは絶対に何も購入しません。

+0

私はジェネリックとテンプレートが非常に異なっていることを理解しています。私はちょうどC#の学習を始めました。これらのような実験は、どのように動作するかを理解するのに役立ちます。制約チェックの「必要性」は、「設定」クラス(限定されたタイプのセットTに対して文字列からTへのマッピングを管理する)の代替案を試すことに由来します。設計基準はa.o. (1)クライアントコードは、サポートされていない型の設定を取得するための呼び出しをキャストする必要はありません(2)コードの重複はありません。 1つのオプションは 'mySettings.get (" settingName ")'のようなものです。ここでは型チェックが便利です。 –

+0

@RenévanOostrum対象の型への文字列変換を実装するほど、.NETは[StringConverter](http://msdn.microsoft.com/en-us/library/9k3hsy2h.aspx)という非常に優れたクラスを提供します。オブジェクトを返すメソッド。あなたの 'get 'メソッドはそれを 'T'に型キャストできます。 – dasblinkenlight

+0

私は変換を考えていませんでしたが、ルックアップの設定クラスには限定されたTのセットのDictionary メンバーがいくつかあります。 get 関数は、値を返す前に検証を行います。このようにして、get_int(文字列)、get_bool(文字列)などを書く必要はありません。サポートされていないタイプのチェックはで実行時には簡単ですが、サポートされていないTではを得るのが簡単です。それで実験が始まったのです。 –

0

あなたがしようとしていることがどのようにジェネリックで可能かはわかりません。このようジェネリッククラス(コンパイルされません、それは例えばここにある)の目的であるもの

:それは「TはAとBとCでなければならない」を意味している場合

public class GenericClass<T> 
    where T : A 
    where T : B 
    where T : C 
{ 
    public T MyMember { get; set; } 

    public GenericClass(T myMember) 
    { 
     this.MyMember = myMember; 
    } 
} 
  • 、 C#ではマルチ継承が存在しないため、不可能です。それが意味している場合
  • そして、「TはAまたはBまたはCでなければなりません」:あなたにそこMyMemberで何ロジックを実装します:A、B、Cは共通点がない場合は
    • は、その後、GenericClassは何の目的を持っていません3つのクラスに共通するものは何もありませんか?
    • A、B、anc Cに共通するものがある場合、すべてのクラスが特定のインターフェイスを実装するか、同じ基本クラスから継承されます。where制約にこのインターフェイスまたはベースクラスを使用します。
0

C#ジェネリック機能のみ制約に基づいて型チェックをパスする必要があるので、これは、動作しません。ジェネリックスは特殊化されていないため、実際の型パラメータは考慮されません。

あなたが約束したことは(暗黙のうちに)T : objectです。

ので、コンパイラの型チェック

CompileTimeAssert<object>.isContainedIn<TypeList<string, int, bool>>(); 

と、この(正しくは)コンパイルに失敗しました。

あなたが書くことができる一方:

void f<T>() where T : Form 
{ 
    CompileTimeAssert<T>.isContainedIn<TypeList<string, Control>>(); 
} 

、すべてFormがあり、以来Controlそれは、コンパイルします。

関連する問題