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

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

69 голосов
/ 12 августа 2008

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

Теперь я знаю, что вы можете ограничить универсальный параметр типа реализацией типа или интерфейса с помощью предложения , где . Тем не менее, это не соответствует требованиям для примитивов (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 - Хороший вопрос, и кое-что, что я уже рассматривал ... Я уверен, что есть универсальный метод, который можно использовать для определения, является ли тип значением или ссылочным типом.

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

Вот оно:

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 (чего мы не делаем использовать на работе).

Особая благодарность Джону Лимджапу за это!

Ответы [ 8 ]

73 голосов
/ 12 августа 2008
public class Class1<GenericType> where GenericType : struct
{
}

Этот, казалось, сделал работу ..

39 голосов
/ 12 августа 2008

Примитивы указываются в перечислении 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;
        }
    }
}

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

21 голосов
/ 12 августа 2008

Почти то, что @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 голосов
/ 16 сентября 2008

Если вы можете терпеть использование фабричных методов (вместо конструкторов 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 выше.

3 голосов
/ 26 марта 2009

@ Роб, 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's 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
2 голосов
/ 27 июля 2012

Имея подобную проблему, мне было интересно, что вы, ребята, думаете о IConvertible интерфейсе . Это позволяет то, что требует запрашивающая сторона, и вы можете расширять с помощью своих собственных реализаций.

Пример:

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

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

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

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

2 голосов
/ 20 октября 2008

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

2 голосов
/ 12 августа 2008

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

...