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

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


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

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


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

  • Boolean
  • байт
  • Char
  • DateTime
  • Decimal
  • двойной
  • Int (16, 32 и 64-бит)
  • тип sbyte
  • один (поплавок)
  • строка
  • UInt (16, 32 и 64-бит)

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

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


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

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

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

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();
        }
    }

помните, что не используйте тип объекта, а универсальный тип для типа параметра. В противном случае вы можете получить нулевое исключение в codeline Type.GetTypeCode(value.GetType()), когда значение null.


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

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