Проверка общего типа
Есть ли способ принудительно / ограничить типы, которые передаются примитивам? (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 - Хороший момент, и то, что я уже обдумывал ... Я уверен, что есть общий метод, который можно использовать, чтобы определить, является ли тип типом значения или ссылочным типом ..
Это может быть полезно для мгновенного удаления множества объектов, с которыми я не хочу иметь дело (но тогда вам нужно беспокоиться об используемых структурах, таких как размер ). Интересная проблема, нет? :)
Вот:
where T : struct
Взято из MSDN .
Мне любопытно ... Можно ли это сделать в .NET 3.x с помощью методов расширения? Создайте интерфейс и реализуйте его в методах расширения (которые, вероятно, будут чище, чем толстый переключатель). Кроме того, если впоследствии вам потребуется расширить его до любых облегченных настраиваемых типов, они также могут реализовать тот же интерфейс без каких-либо изменений в базовом коде.
Что вы ребята думаете?
Печальная новость: я работаю в Framework 2 !! : D
РЕДАКТИРОВАТЬ 3
Это было так просто, вслед за Джоном Лимджапсом Пойнтером ... Так просто, я почти хочу плакать, но это здорово, потому что код работает как шарм!
Вот что я сделал (вы будете смеяться!):
Код добавлен в общий класс
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)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;
}
}
}
Это все еще уродливый переключатель. Но это хорошее место для начала!
Столкнувшись с подобной проблемой, мне было интересно, как вы, ребята, относитесь к интерфейсу IConvertible . Он позволяет то, что требует запрашивающий, и вы можете расширять свои собственные реализации.
Пример:
public class MyClass<TKey>
where TKey : IConvertible
{
// class intentionally abbreviated
}
Я думаю об этом как о решении, хотя многие из предложенных также были частью моего выбора.
Однако меня беспокоит, вводит ли это в заблуждение потенциальных разработчиков, использующих ваш класс?
Ура - и спасибо.
Примерно то, что уже сказал @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.
Используйте настраиваемое правило FxCop, которое отмечает нежелательное использование MyClass<>
.
Если вы можете терпеть использование фабричных методов (вместо конструкторов 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>
asMyClass.MyClass<T>
. - Используйте
MyClass<T>
как дано. Создайте статический класс,MyClass
который вызывает статические методы приMyClass<T>
использованииMyClass<AnyTypeItDoesntMatter>
(возможно, каждый раз используя соответствующий тип, просто для хихиканья). - (Проще, но определенно странно) Создайте абстрактный тип,
MyClass
наследующий отMyClass<AnyTypeItDoesntMatter>
. (Скажем, для конкретностиMyClass<int>
.) Поскольку вы можете вызывать статические методы, определенные в базовом классе, через имя производного класса, теперь вы можете использоватьMyClass.FromString
.
Это дает вам статическую проверку за счет большего количества записей.
Если вам нравится динамическая проверка, я бы использовал вариант решения TypeCode выше.
Вы можете упростить EnforcePrimitiveType
метод, используя typeof(PrimitiveDataType).IsPrimitive
свойство. Я что-то упускаю?
@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