Общая проверка типов
-
08-06-2019 - |
Вопрос
Есть ли способ обеспечить/ограничить типы, передаваемые примитивам? (bool, int, строка и т. д.)
Теперь я знаю, что вы можете ограничить параметр универсального типа реализацией типа или интерфейса с помощью где пункт.Однако это не соответствует требованиям примитивов (AFAIK), поскольку не все они имеют общую основу (кроме объект прежде чем кто-то скажет!:П).
Итак, мои текущие мысли — просто стиснуть зубы и сделать большое дело. выключатель заявление и бросить АргументИсключение при неудаче..
РЕДАКТИРОВАТЬ 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 (которым мы не пользуемся на работе).
Особая благодарность Джону Лимджапу за это!
Решение
Примитивы, по-видимому, указаны в 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;
}
}
}
Это все равно уродливый переключатель.Но это хорошее место для начала!
Другие советы
public class Class1<GenericType> where GenericType : struct
{
}
Этот вроде бы справился с задачей..
Примерно то же, что @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.
Если вы можете терпеть использование фабричных методов (вместо запрошенных вами конструкторов 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, описанного выше.
@Роб, 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
Вы можете упростить EnforcePrimitiveType
метод с использованием typeof(PrimitiveDataType).IsPrimitive
свойство.Я что-то пропустил?
Используйте индивидуальный FXCop правило, которое сигнализирует о нежелательном использовании MyClass<>
.
Имея похожую проблему, мне было интересно, что вы, ребята, думаете о IКонвертируемый интерфейс.Он позволяет выполнять требования запрашивающей стороны, и вы можете расширить его своими собственными реализациями.
Пример:
public class MyClass<TKey>
where TKey : IConvertible
{
// class intentionally abbreviated
}
Я думаю об этом как о решении, хотя многие из предложенных вариантов также были частью моего выбора.
Однако меня беспокоит то, не вводит ли это в заблуждение потенциальных разработчиков, использующих ваш класс?
Приветствую - и спасибо.