2012-01-05 12 views
28

値型と文字列にのみ適用されるIEnumerableに拡張メソッドを書き込もうとしています。C#値型と文字列を含める一般的な制約

public static string MyMethod<T>(this IEnumerable<T> source) where T : struct, string 

ただし、 'string'は、密封されたクラスであるため、有効な制約ではありません。

これを行う方法はありますか?

編集:

私は実際にやろうとしていることは動的に構築SQLで「IN」句の値のリストを準備しています。

は私がクリーンアップする次のようなコードのインスタンスの多くを持っている:

ToSQLは()SQLインジェクションを処理するコードを持っている
sb.AppendLine(string.Format("AND value IN ({0})", string.Join(",", Values.Select(x => x.ToSQL()).ToArray()))); 

+0

あなたの実装では、値の型と文字列は他のものと違うところで受け入れられますか? –

答えて

22

いいえ、できません。つまり、すべての制約が満たされていなければならないことを意味する場合は、一般的な制約は常に「AND」であるため、開封されていないクラスを使用しようとしてもこれは失敗します。

なぜこれをやりたいですか?おそらくもっとうまくいく別のアプローチがあります。

+0

ありがとうございます。何が最良の選択肢でしょうか? 2つの別々の方法? –

+0

@ポーズ:私はまずSQLに値を書式化しないでください。代わりに、パラメータ化されたクエリを使用するようにリファクタリングすることをお勧めします。 –

+0

最初にそのルートをダウンしようとしました。しかし、リストをパラメータとしてSQL Serverに渡すことや、値の中に有効なテキストである可能性のあるものに分割する必要があるため、私たちのアプローチが変わりました。 SQLは、条件付き結合などを使用して動的に構築されていますが、ストアドプロシージャではなくコードで行う方がよいと感じました。これは、それが静的SQLにすることができない理由でそれにスローされたパラメータの多くの順列を持つことができるクエリです。 –

32

あなたは、2つの別々の方法を定義する必要があります。

public static string MyMethod<T>(this IEnumerable<T> source) where T : struct 
public static string MyMethod(this IEnumerable<string> source) 
+1

また、これらのメソッドの両方が物事を少し乾燥した状態に保つために呼び出す3番目のプライベートメソッドを持つこともできます。同様の質問に[この回答](http://stackoverflow.com/a/4109547/957950)を参照してください。 – brichins

44

たぶん、あなたはIConvertibleタイプに制限することができましたか?すべてのシステムにもインタフェースを実装し、これらのインターフェイスの方法を用いて変換することができるプリミティブので、Tを必要とするこの制限は、以下の一つである:

  • ブール
  • バイト
  • チャー
  • 日時
  • 小数点
  • ダブル
  • INT(16、32および64ビット)
  • SByte
  • シングル(フロート)
  • 文字列
  • UINT(16、32および64ビット)

あなたがIConvertibleを持っているかのように、チャンスは、それはこれらのタイプの一つだ非常に優れていますIConvertibleインターフェイスは、サードパーティのタイプではめったに行われないという痛みです。

主な欠点は、実際にTをこれらの型のインスタンスに変換することなく、ObjectやIConvertibleメソッド、またはObjectまたはIConvertibleをとるメソッドを呼び出すことがすべてのメソッドで分かることです。何かもっと必要なものがあれば(+を使って追加したり連結したりする能力のように)、構造体タイプに2つのメソッドを設定し、文字列に強くタイプする2つのメソッドを設定するだけで、全体的に最善の策になると思います。

+1

素晴らしいアイデア!私はそれを考えなかった。 –

8

私は、ハックソリューション:インターフェースを使用しました。 を参照してください。組み込みの値型と文字列型のインターフェイスが実装されています:

struct Int32 : IComparable, IFormattable, IConvertible, IComparable<int>, IEquatable<int> 

class String : IComparable, ICloneable, IConvertible, IComparable<string>, IEnumerable<char>, IEnumerable, IEquatable<string> 

struct Boolean : IComparable, IConvertible, IComparable<bool>, IEquatable<bool> 

struct DateTime : IComparable, IFormattable, IConvertible, ISerializable, IComparable<DateTime>, IEquatable<DateTime> 

struct UInt64 : IComparable, IFormattable, IConvertible, IComparable<ulong>, IEquatable<ulong> 

struct Single : IComparable, IFormattable, IConvertible, IComparable<float>, IEquatable<float> 

struct Byte : IComparable, IFormattable, IConvertible, IComparable<byte>, IEquatable<byte> 

struct Char : IComparable, IConvertible, IComparable<char>, IEquatable<char> 

struct Decimal : IFormattable, IComparable, IConvertible, IComparable<decimal>, IEquatable<decimal> 

をあなたは、制約のためIComparable,IConvertible,IEquatable<T>を使用することができます。このよう :

public static void SetValue<T>(T value) where T : IComparable, IConvertible, IEquatable<T> 
    { 
     //TODO: 
    } 

それとも、制約なしにデータの時間を確認するタイプコードを使用することができます。

public static void SetValue<T>(T value) 
    { 
     switch (Type.GetTypeCode(typeof(T))) 
     { 
      #region These types are not what u want, comment them to throw ArgumentOutOfRangeException 

      case TypeCode.Empty: 
       break; 
      case TypeCode.Object: 
       break; 
      case TypeCode.DBNull: 

       #endregion 

       break; 
      case TypeCode.Boolean: 
       break; 
      case TypeCode.Char: 
       break; 
      case TypeCode.SByte: 
       break; 
      case TypeCode.Byte: 
       break; 
      case TypeCode.Int16: 
       break; 
      case TypeCode.UInt16: 
       break; 
      case TypeCode.Int32: 
       break; 
      case TypeCode.UInt32: 
       break; 
      case TypeCode.Int64: 
       break; 
      case TypeCode.UInt64: 
       break; 
      case TypeCode.Single: 
       break; 
      case TypeCode.Double: 
       break; 
      case TypeCode.Decimal: 
       break; 
      case TypeCode.DateTime: 
       break; 
      case TypeCode.String: 
       break; 
      default: 
       throw new ArgumentOutOfRangeException(); 
     } 
    } 

オブジェクト型ではなく、汎用型をパラメータ型として使用することを忘れないでください。それ以外の場合は、値がnullの場合、コード行Type.GetTypeCode(value.GetType())でNULL EXCEPTIONが返されることがあります。

0

クラスの使用時に、静的コンストラクタを使用して型パラメータをチェックできます。

class Gen<T> { 
    static Gen() { 
     if (!typeof(T).IsValueType && typeof(T) != typeof(String)) 
     { 
      throw new ArgumentException("T must be a value type or System.String."); 
     } 
    } 
} 
+2

これはコンパイル時には役に立ちません。これは、ジェネリックが実際に使用されるべきものです。また、コンストラクタ、特に静的なコンストラクタで例外をスローすることは非常に失礼です。コンシューマは、実行時に「役に立たない」TypeInitializerExceptionを取得する可能性が非常に高く、その理由はわかりません。 –

関連する問題