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

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

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

Итак, мои нынешние мысли состоят в том, чтобы просто стиснуть зубы, сделать большое заявление о переключении и выдать 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 (которые мы не используем на работе).

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

12.08.2008 15:07:40
Вы также можете вызвать проверку в своем статическом конструкторе, поэтому она вызывается только один раз для каждого типа, используемого в качестве общего аргумента.
Julien 20.02.2012 08:08:29
8 ОТВЕТОВ
РЕШЕНИЕ

Примитивы, по-видимому, указываются в 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;
        }
    }
}

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

39
18.04.2013 13:18:09
Ну, я не знаю, как, но основной вопрос между объектами и примитивами состоит в том, что большинство примитивов на самом деле являются структурами, а не классами. Я буду искать способ понять их программно :)
Jon Limjap 12.08.2008 15:14:15
Хороший вопрос, и кое-что, что я уже рассматривал ... Я уверен, что есть универсальный метод, который можно использовать, чтобы определить, является ли тип значением или ссылочным типом. Это может быть полезно для мгновенного удаления большого количества объектов Я не хочу иметь дело с (но тогда вам нужно беспокоиться о таких структурах, как Размер ). Интересной проблемы нет? :) РЕДАКТИРОВАТЬ: Ах да, вот оно: где T: структура взято из MSDN .
Rob Cooper 12.08.2008 15:17:53
Мне любопытно ... Интересно, можно ли это сделать в .NET 3.x с помощью методов расширения ... Создать интерфейс ... Реализовать интерфейс в методах расширения ... (который, вероятно, будет чище, чем немного жирный переключатель) Плюс, если вам затем потребуется расширить на любые легковесные пользовательские типы, они также могут реализовать тот же интерфейс, без изменений в базовом коде. Ребята, что вы думаете? Печальная новость - я работаю в рамках 2 !! : D РЕДАКТИРОВАТЬ: Покидая офис примерно через 5 минут, заберу это, когда я вернусь домой! : D
Rob Cooper 12.08.2008 15:23:34
> открытый класс Class1 <GenericType>, где> GenericType: struct {}>> Это, кажется, делает работу .. Проблема, которую я вижу с этой реализацией, заключается в том, что если у вас есть пользовательская структура, она все равно будет выглядеть как примитив, который это не тот случай.
Jon Limjap 12.08.2008 16:03:30
public class Class1<GenericType> where GenericType : struct
{
}

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

74
12.08.2008 15:21:59

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

2
23.05.2011 10:56:42

Почти то, что @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.

22
23.05.2011 11:11:24

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

4
16.09.2008 17:18:20

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

2
19.10.2008 22:43:51
Передача строки в этот оператор возвращает False, и автор указывает, что ему нужна строка для возврата True. .NET считает char примитивным, но не строкой.
Nicholas 5.11.2008 10:55:49

@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'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
3
3.01.2011 12:42:15

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

Пример:

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

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

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

Ура - и спасибо.

2
27.07.2012 14:53:38