it-roy-ru.com

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

Я пытаюсь написать метод расширения в IEnumerable, который будет применяться только к типам значений и строкам.

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

Однако 'string' не является допустимым ограничением, поскольку это закрытый класс.

Есть какой-либо способ сделать это?

Правка:

На самом деле я пытаюсь подготовить список значений для предложения «IN» в динамически сконструированном SQL.

У меня есть много примеров кода, таких как следующий, который я хочу очистить:

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

Где ToSQL () имеет код для обработки SqlInjection.

36
Brett Postin

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

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

27
Jon Skeet

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

  • Логический 
  • Байт 
  • Голец 
  • DateTime 
  • Десятичный 
  • Двойной 
  • Int (16, 32 и 64-разрядный) 
  • SByte 
  • Single (плавать) 
  • Строка 
  • UInt (16, 32 и 64-битный)

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

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

56
KeithS

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

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

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

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>for ограничений . Вот так:

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

11
Sean C.

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

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