C#包含值类型AND字符串的泛型约束

本文关键字:泛型 约束 字符串 AND 包含值 类型 | 更新日期: 2023-09-27 18:19:28

我正试图在IEnumerable上编写一个仅适用于值类型和字符串的扩展方法。

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

但是,"string"不是有效的约束,因为它是一个密封类。

有办法做到这一点吗?

编辑:

我实际要做的是为动态构建的SQL中的"IN"子句准备一个值列表。

我有很多代码实例,比如下面我想清理的:

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

其中ToSQL()具有处理SqlInjection的代码。

C#包含值类型AND字符串的泛型约束

也许您可以限制为IConvertable类型?所有可以使用这些接口方法转换的系统原语也实现了接口,因此这种限制要求T是以下之一:

  • 布尔值
  • 字节
  • Char
  • 日期时间
  • 小数
  • Double
  • Int(16、32和64位)
  • SByte
  • 单个(浮动)
  • 字符串
  • UInt(16、32和64位)

如果你有一个IConvertable,它很可能是这些类型中的一个,因为IConverable接口的实现非常困难,很少对第三方类型这样做。

主要的缺点是,如果不将T实际转换为其中一种类型的实例,您的方法所知道的就是调用Object和IConvertable方法,或者采用Object或IConverable的方法。如果你需要更多的东西(比如使用+添加和/或连接的能力),我认为简单地设置两个方法,一个是结构类型的泛型方法,另一个是字符串的强类型方法,将是最好的选择。

您需要定义两个独立的方法:

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

不,你不能。如果你明白我的意思的话,泛型约束总是"AND"-ed的(即所有约束都必须满足),所以即使你试图使用一些未密封的类,这仍然会失败。

你为什么要这么做?也许还有另一种方法会更好。

我使用了一个破解解决方案:接口。查看内置值类型和字符串类型实现的接口:

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异常。

使用类时,可以使用静态构造函数来检查类型参数。

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.");
        }
    }
}