2012-01-05 79 views
28

我想在IEnumerable上寫一個只適用於值類型和字符串的擴展方法。C#通用約束包含值類型和字符串

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

但'字符串'不是一個有效的約束,因爲它是一個密封的類。

有沒有辦法做到這一點?

編輯:

什麼實際上,我試圖做的是在動態構建SQL的「IN」的條款準備值的列表。

我有很多的代碼實例,如下面,我要清理:

sb.AppendLine(string.Format("AND value IN ({0})", string.Join(",", Values.Select(x => x.ToSQL()).ToArray()))); 

凡ToSQL()的代碼來處理SQL注入攻擊。

+0

對於您的實現,什麼使值類型和字符串在別人不可用的地方接受? – 2012-01-05 15:58:34

回答

22

不,你不能。如果你明白我的意思(即所有約束都必須滿足),那麼通用約束始終是「AND」的,所以即使你嘗試使用一些非密封類,這仍然會失敗。

你爲什麼要這樣做?也許還有另一種更好的方法。

+0

謝謝。什麼是最好的選擇?兩種獨立的方法? – 2012-01-05 16:00:26

+0

@Poz:鑑於我不會將值格式化爲SQL,我建議重構使用參數化查詢來代替... – 2012-01-05 16:12:18

+0

我們最初試圖沿着這條路線走下去。然而,在SQL Server中將通過列表作爲參數的問題,以及需要在值中分隔可能是有效文本的內容,這使我們改變了方法。 SQL也是動態構建的,使用條件連接等,我們認爲在代碼中而不是在存儲過程中會更好。這是一個查詢,可以有很多排列的參數拋出它,這就是爲什麼我們不能讓它靜態SQL。 – 2012-01-05 16:27:29

32

您需要定義2種獨立的方法:

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

你也可以使用第三種私有方法,這兩種方法都是爲了讓事情保持乾爽。看到[這個答案](http://stackoverflow.com/a/4109547/957950)到一個類似的問題。 – brichins 2017-01-25 15:55:06

44

也許你可以限制IConvertible類型?所有系統原語可以使用這些接口中的方法也實現接口轉換,所以這個限制將需要噸至爲以下之一:

  • 布爾
  • 字節
  • 字符
  • 日期時間
  • 十進制
  • INT(16,32和64位)
  • 爲SByte
  • 單(浮動)
  • 字符串
  • UINT(16,32位和64位)

如果你有一個IConvertible,機會是非常好的是這些類型之一,作爲IConvertible接口很難實現,很少用於第三方類型。

主要缺點是,如果沒有將T實際轉換爲其中一種類型的實例,那麼您所有的方法都會知道如何調用Object和IConvertible方法或採用Object或IConvertible的方法。如果你還需要更多的東西(比如使用+添加和/或連接的能力),我認爲只需設置兩個方法,一個是泛型​​類型,另一個是強類型類型的字符串,這將是總體上最好的選擇。

+1

好主意!我沒有想到這一點。 – 2013-03-11 20:02:55

8

我使用了hack-solution:接口。 見內置值類型和字符串類型已實現的接口:

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(); 
     } 
    } 

請記住,不要使用對象類型,但對參數類型使用泛型類型。否則,當值爲空時,您可能會在代碼行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」,並且不知道爲什麼。 – 2016-11-30 01:37:34