Проверка общего типа

Есть ли способ принудительно ввести/ограничить типы, которые передаются примитивам? (bool, int, string и т.д.)

Теперь я знаю, что вы можете ограничить общий тип параметра реализацией типа или интерфейса с помощью предложения where. Однако это не соответствует законопроекту для примитивов (AFAIK), потому что у них не все есть общее основание (кроме объекта, прежде чем кто-то говорит!: P).

Итак, мои текущие мысли состоят в том, чтобы просто скрестить зубы и сделать большой оператор switch и бросить ArgumentException при неудаче.


РЕДАКТИРОВАТЬ 1:

Просто уточнить:

Определение кода должно быть следующим:

public class MyClass<GenericType> ....

И создание экземпляра:

MyClass<bool> = new MyClass<bool>(); // Legal
MyClass<string> = new MyClass<string>(); // Legal
MyClass<DataSet> = new MyClass<DataSet>(); // Illegal
MyClass<RobsFunkyHat> = new MyClass<RobsFunkyHat>(); // Illegal (but looks awesome!)

РЕДАКТИРОВАТЬ 2

@Jon Limjap - Хорошая точка и что-то, что я уже рассматривал. Я уверен, что существует общий метод, который может быть использован для определения того, имеет ли тип значение или ссылочный тип.

Это может быть полезно при мгновенном удалении множества объектов, с которыми я не хочу иметь дело (но тогда вам нужно беспокоиться о структурах, которые используются, например, в Size). Интересная проблема нет?:)

Вот он:

where T : struct

Взято из MSDN.


Мне любопытно.. Это можно сделать в .NET 3.x с использованием методов расширения? Создайте интерфейс и реализуйте интерфейс в методах расширения (который, вероятно, будет более чистым, чем бит-переключатель). Плюс, если вам нужно впоследствии расширить любые легкие пользовательские типы, они также могут реализовать тот же интерфейс, без изменений, необходимых для базового кода.

Что вы, ребята, думаете?

Печальная новость: я работаю в Framework 2!!: D


РЕДАКТИРОВАТЬ 3

Это было так просто, следуя из Jon Limjaps Pointer. Так просто, я почти хочу плакать, но это здорово, потому что код работает как шарм!

Итак, вот что я сделал (вы будете смеяться!):

Код, добавленный в общий класс

bool TypeValid()
{
    // Get the TypeCode from the Primitive Type
    TypeCode code = Type.GetTypeCode(typeof(PrimitiveDataType));

    // All of the TypeCode Enumeration refer Primitive Types
    // with the exception of Object and Empty (Null).
    // Since I am willing to allow Null Types (at this time)
    // all we need to check for is Object!
    switch (code)
    {
        case TypeCode.Object:
            return false;
        default:
            return true;
    }
}

Затем небольшой метод полезности для проверки типа и исключения исключений,

private void EnforcePrimitiveType()
{
    if (!TypeValid())
        throw new InvalidOperationException(
            "Unable to Instantiate SimpleMetadata based on the Generic Type of '" + typeof(PrimitiveDataType).Name + 
            "' - this Class is Designed to Work with Primitive Data Types Only.");
}

Все, что нужно сделать, это вызвать EnforcePrimitiveType() в конструкторах классов. Работа выполнена!: -)

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

Особая благодарность Jon Limjap за это!

Ответ 1

Примитивы, как представляется, указаны в TypeCode перечисления:

Возможно, есть способ узнать, содержит ли объект TypeCode enum без необходимости передавать его конкретному объекту или вызывать GetType() или typeof()?

Обновление Это было прямо под моим носом. В этом примере кода показано следующее:

static void WriteObjectInfo(object testObject)
{
    TypeCode    typeCode = Type.GetTypeCode( testObject.GetType() );

    switch( typeCode )
    {
        case TypeCode.Boolean:
            Console.WriteLine("Boolean: {0}", testObject);
            break;

        case TypeCode.Double:
            Console.WriteLine("Double: {0}", testObject);
            break;

        default:
            Console.WriteLine("{0}: {1}", typeCode.ToString(), testObject);
            break;
        }
    }
}

Это все еще уродливый переключатель. Но это хорошее место для начала!

Ответ 2

public class Class1<GenericType> where GenericType : struct
{
}

Казалось, что это сработало.

Ответ 3

Довольно многое, что уже сказал @Lars:

//Force T to be a value (primitive) type.
public class Class1<T> where T: struct

//Force T to be a reference type.
public class Class1<T> where T: class

//Force T to be a parameterless constructor.
public class Class1<T> where T: new()

Все работают в .NET 2, 3 и 3.5.

Ответ 4

Если вы можете терпеть использование методов factory (вместо запрошенных вами конструкторов MyClass), вы всегда можете сделать что-то вроде этого:

class MyClass<T>
{
  private readonly T _value;

  private MyClass(T value) { _value = value; }

  public static MyClass<int> FromInt32(int value) { return new MyClass<int>(value); }
  public static MyClass<string> FromString(string value) { return new MyClass<string>(value); }
  // etc for all the primitive types, or whatever other fixed set of types you are concerned about
}

Проблема заключается в том, что вам нужно будет ввести MyClass<AnyTypeItDoesntMatter>.FromInt32, что раздражает. Существует не очень хороший способ обойти это, если вы хотите сохранить частную конструктор конструктора, но вот несколько обходных решений:

  • Создайте абстрактный класс MyClass. Сделайте MyClass<T> наследовать от MyClass и вставьте его в MyClass. Переместите статические методы на MyClass. Это обеспечит всю видимость за счет доступа к MyClass<T> как MyClass.MyClass<T>.
  • Используйте MyClass<T> как указано. Создайте статический класс MyClass, который вызывает статические методы в MyClass<T>, используя MyClass<AnyTypeItDoesntMatter> (возможно, каждый раз используя соответствующий тип, только для хихиканья).
  • (Легче, но, конечно, странно) Создайте абстрактный тип MyClass , который наследует от MyClass<AnyTypeItDoesntMatter>. (Для конкретности, скажем, MyClass<int>.) Поскольку вы можете вызвать статические методы, определенные в базовом классе, через имя производного класса, теперь вы можете использовать MyClass.FromString.

Это дает вам статическую проверку за счет большей записи.

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

Ответ 5

@Rob, Enum проскальзывает через функцию TypeValid, поскольку она TypeCode равна Integer. Я обновил функцию, чтобы также проверить Enum.

Private Function TypeValid() As Boolean
    Dim g As Type = GetType(T)
    Dim code As TypeCode = Type.GetTypeCode(g)

    ' All of the TypeCode Enumeration refer Primitive Types
    ' with the exception of Object and Empty (Nothing).
    ' Note: must also catch Enum as its type is Integer.
    Select Case code
        Case TypeCode.Object
            Return False
        Case Else
            ' Enum TypeCode is Integer, so check BaseType
            If g.BaseType Is GetType(System.Enum) Then
                Return False
            Else
                Return True
            End If
    End Select
End Function

Ответ 6

Используйте настраиваемое FxCop правило, которое указывает на нежелательное использование MyClass<>.

Ответ 7

Вы можете упростить метод EnforcePrimitiveType, используя свойство typeof(PrimitiveDataType).IsPrimitive. Я что-то пропустил?

Ответ 8

Имея аналогичную задачу, мне было интересно, как вы, ребята, относились к IConvertible interface. Он позволяет то, что требует запросчик, и вы можете расширить свои собственные реализации.

Пример:

    public class MyClass<TKey>
    where TKey : IConvertible
{
    // class intentionally abbreviated
}

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

Моя забота - однако - это вводит в заблуждение потенциальных разработчиков, использующих ваш класс?

Приветствия - и спасибо.