2012-01-05 94 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