С# Общие ограничения для включения типов значений и строк
Я пытаюсь написать метод расширения в IEnumerable, который будет применяться только к типам значений и строкам.
public static string MyMethod<T>(this IEnumerable<T> source) where T : struct, string
Однако 'string' не является допустимым ограничением, так как это закрытый класс.
Есть ли способ сделать это?
Edit:
То, что я на самом деле пытаюсь сделать, - это подготовить список значений для предложения "IN" в динамически построенном SQL.
У меня есть много примеров кода, например следующего: я хочу очистить:
sb.AppendLine(string.Format("AND value IN ({0})", string.Join(",", Values.Select(x => x.ToSQL()).ToArray())));
Где ToSQL() имеет код для обработки SqlInjection.
Ответы
Ответ 1
Нет, вы не можете. Общие ограничения всегда "AND" -ed, если вы понимаете, что я имею в виду (т.е. Все ограничения должны быть удовлетворены), поэтому даже если вы пытаетесь использовать какой-то незапечатанный класс, это все равно потерпит неудачу.
Почему вы хотите это сделать? Возможно, есть другой подход, который будет работать лучше.
Ответ 2
Возможно, вы можете ограничить типы IConvertible? Все системные примитивы, которые могут быть преобразованы с использованием этих методов интерфейса, также реализуют интерфейс, поэтому это ограничение потребовало бы, чтобы T было одним из следующих:
- Boolean
- Байт
- Char
- DateTime
- Десятичный
- Двойной
- Int (16, 32 и 64-бит)
- SByte
- Одиночный (плавающий)
- Строка
- UInt (16, 32 и 64-бит)
Если у вас есть IConvertible, скорее всего, это один из этих типов, так как интерфейс IConvertible настолько болен, что его редко делают для сторонних типов.
Основной недостаток заключается в том, что без фактического преобразования T в экземпляр одного из этих типов весь ваш метод будет знать, как это сделать, это вызвать методы Object и IConvertible или методы, которые принимают объект или IConvertible. Если вам нужно что-то большее (например, возможность добавления и/или конкатенации с помощью +), я думаю, что просто настройка двух методов, один общий для типов структуры и второй строго типизированный для строк, будет лучшим вариантом в целом.
Ответ 3
Вам нужно определить два разных метода:
public static string MyMethod<T>(this IEnumerable<T> source) where T : struct
public static string MyMethod(this IEnumerable<string> source)
Ответ 4
Я использовал интерфейс 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();
}
}
Помните, что не используйте тип объекта, а тип типа для типа параметра. В противном случае вы можете получить NULL EXCEPTION в кодовой строке Type.GetTypeCode(value.GetType())
, когда значение равно null.
Ответ 5
Вы можете использовать статический конструктор для проверки параметра типа при использовании класса.
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.");
}
}
}