C # Общие ограничения для включения типов значений и строк - PullRequest
41 голосов
/ 05 января 2012

Я пытаюсь написать метод расширения в 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.

Ответы [ 5 ]

62 голосов
/ 05 января 2012

Может быть, вы можете ограничиться IConvertible типами?Все системные примитивы, которые могут быть преобразованы с использованием этих методов интерфейса, также реализуют интерфейс, поэтому это ограничение потребует от T одного из следующих:

  • Boolean
  • Byte
  • Char
  • DateTime
  • Десятичное число
  • Двойное
  • Int (16, 32 и 64-битное)
  • SByte
  • Single (float)
  • String
  • UInt (16, 32 и 64-битный)

Если у вас есть IConvertible, шансы ОЧЕНЬ хорошиэто один из этих типов, так как интерфейс IConvertible настолько труден для реализации, что его редко делают для сторонних типов.

Основной недостаток заключается в том, что без преобразования T в экземпляр одного из этих типов,все ваши методы будут знать, как это делать, это вызывать методы Object и IConvertible или методы, которые принимают Object или IConvertible.Если вам нужно что-то большее (например, возможность добавлять и / или объединять с помощью +), я думаю, что в целом лучшим выбором будет простая установка двух методов, одного общего для структурных типов и второго строго типизированного для строк.

38 голосов
/ 05 января 2012

Вам нужно определить 2 отдельных метода:

public static string MyMethod<T>(this IEnumerable<T> source) where T : struct
public static string MyMethod(this IEnumerable<string> source)
28 голосов
/ 05 января 2012

Нет, вы не можете. Общие ограничения всегда начинаются с «И», если вы понимаете, что я имею в виду (т.е. все ограничения должны быть выполнены), так что даже если вы пытаетесь использовать какой-то незапечатанный класс, это все равно не получится.

Почему ты хочешь это сделать? Возможно, есть другой подход, который будет работать лучше.

15 голосов
/ 05 июня 2015

Я использовал хак-решение: интерфейс. Посмотрите интерфейсы, которые реализовали встроенные типы значений и строковый тип:

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()), когда значение равно нулю.

2 голосов
/ 01 июля 2016

Вы можете использовать статический конструктор для проверки параметра типа при использовании класса.

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