Проверка общего типа
Есть ли способ принудительно ввести/ограничить типы, которые передаются примитивам? (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
}
Я думаю об этом как о решении, хотя многие из предложенных были частью моего выбора.
Моя забота - однако - это вводит в заблуждение потенциальных разработчиков, использующих ваш класс?
Приветствия - и спасибо.