Универсальный тип ограничения параметр в C#.NET

рассмотрим следующий универсальный класс:

public class Custom<T> where T : string
{
}

Это приводит к следующей ошибке:

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

есть ли другой способ ограничить типы, которые может использовать мой общий класс?

кроме того, могу ли я ограничить несколько типов?

Г. Е.

T может быть только строка, int или byte

3 ответов


public class Custom<T> where T : string

не допускается, потому что только T это соответствует тому, что: string (string is sealed) - что делает его довольно бессмысленно, как универсальный.

кроме того, могу ли я ограничить несколько типов?

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

T can only be string, int or byte

вы может использовать что-то вроде IEquatable<T>, но это не ограничивает его так сильно, как вам хотелось бы, поэтому в конечном итоге: нет.

что-то вы мог бы do-это доступ к нему через перегруженную фабрику:

public abstract class Custom
{
    public static Custom Create(int value)
    { return new CustomImpl<int>(value); }
    public static Custom Create(byte value)
    { return new CustomImpl<byte>(value); }
    public static Custom Create(string value)
    { return new CustomImpl<string>(value); }
    private class CustomImpl<T> : Custom
    {
        public CustomImpl(T val) { /*...*/ }
    }
}

из моего опыта я бы сказал, что понимаю, почему вы хотели бы иметь,string и int ... из-за общего базового класса, имеющего ID типа string или int

но это точно, что это невозможно. Как говорится в этом описании msdn: http://msdn.microsoft.com/en-us/library/d5x73970%28v=vs.80%29.aspx

мы можем иметь constraing class (ссылочный объект типа string) или struct (ValueType как int) Так смешивать string и int не будут возможны

Примечание: ошибка для строки имеет смысл, потому что строка запечатана, поэтому она не должна быть общей - строка ID что нужно


после просмотра ответов здесь и немного поиграть вокруг себя, я придумал следующую реализацию, которая проверяет ограничения во время выполнения, а не во время компиляции.

// This example takes 3 parameters...
public class GenericConstraint<T1, T2, T3>
{
    public GenericConstraint(Type type)
    {
        if (!(type is T1) || !(type is T2) || !(type is T3))
        {
            throw new Exception("This is not a supported type");
        }
    }
}

теперь я наследую это от моего пользовательского класса...

public class Custom<T> : GenericConstraint<string, int, byte>
{
    public Custom() : base(typeof(T))
    {
    }
}

теперь это вызывает ошибку:

Custom<long> item = new Custom<long>();

Это не!

Custom<byte> item2 = new Custom<byte>();

как заявил Марк Гравелл, это не является хорошим использованием наследования или дженериков. Думая об этом логически, наследуя GenericConstraint, это ограничивает наследование только этим, а также не использует иерархию типов должным образом. С точки зрения использования дженериков, это на самом деле довольно бессмысленно!

у меня есть другое решение, которое действует как вспомогательный метод для ограничения типов во время выполнения. Это освобождает объект от наследства и, следовательно, не влияет на иерархию типов.
public static void ConstrainParameterType(Type parameterType, GenericConstraint constraintType, params Type[] allowedTypes)
        {
            if (constraintType == GenericConstraint.ExactType)
            {
                if (!allowedTypes.Contains<Type>(parameterType))
                {
                    throw new Exception("A runtime constraint disallows use of type " + parameterType.Name + " with this parameter.");
                }
            }
            else
            {
                foreach (Type constraint in allowedTypes)
                {
                    if (!constraint.IsAssignableFrom(parameterType))
                    {
                        throw new Exception("A runtime constraint disallows use of type " + parameterType.Name + " with this parameter.");
                    }
                }
            }
        }

public enum GenericConstraint
    {
        /// <summary>
        /// The type must be exact.
        /// </summary>
        ExactType,

        /// <summary>
        /// The type must be assignable.
        /// </summary>
        AssignableType
    }

теперь это позволяет несколько ограничений типа на generic объекты, даже где типы загерметизированы etc.

" пользовательский открытый класс, где T: string ... не допускается, потому что единственный T, который соответствует этому: string (строка запечатана) - создание его довольно бессмысленно как родовое."

Да, это бессмысленно, но в некоторых случаях вы можете ограничить объект, чтобы разрешить, например; String, StringBuilder и SecureString. Хотя это не обеспечивает ограничение времени компиляции, это обеспечивает ограничение времени выполнения и некоторая гибкость в отношении типов, которые могут использоваться в ограничении.