Как проверить, является ли тип примитивным

162

У меня есть блок кода, который сериализует тип в тег HTML.

Type t = typeof(T); // I pass <T> in as a paramter, where myObj is of type T
tagBuilder.Attributes.Add("class", t.Name);
foreach (PropertyInfo prop in t.GetProperties())
{
    object propValue = prop.GetValue(myObj, null);
    string stringValue = propValue != null ? propValue.ToString() : String.Empty;
    tagBuilder.Attributes.Add(prop.Name, stringValue);
}

Это прекрасно работает, за исключением того, что я хочу, чтобы это делалось только для примитивных типов, например int, doubleи boolт. Д., А также для других типов, которые не являются примитивными, но могут быть легко сериализованы, например string. Я хочу, чтобы он игнорировал все остальное, например списки и другие пользовательские типы.

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

DaveDev
источник
12
System.Stringэто не примитивный тип.
SLaks
3
Лучший способ сделать это - вообще не использовать генерики. Если вы поддерживаете небольшое количество типов в качестве допустимых типов параметров, то просто получите столько перегрузок. Если вы поддерживаете любой тип, который реализует ISerializable, то напишите неуниверсальный метод, который принимает ISerializable. Используйте дженерики для вещей, которые на самом деле являются дженериками ; если тип действительно имеет значение, он, вероятно, не является общим.
Эрик Липперт
@Eric: Спасибо, мне также интересно, можно ли использовать те же критерии с цифрами? Например, для написания математических функций, которые поддерживают все числовые типы, т. Е. Среднее, Сумма и т. Д. Должны ли они быть реализованы с использованием общих или перегрузок? Имеет ли значение, является ли реализация такой же или нет? Потому что это в значительной степени та же самая операция для Average, Sum для любого числового типа, верно?
Джоан Венге
1
@Joan: Возможность написания общих арифметических методов для типов, ограниченных для реализации различных операторов, является часто запрашиваемой функцией, но она требует поддержки CLR и на удивление сложна. Мы рассматриваем это для будущих версий языка, но без обещаний.
Эрик Липперт

Ответы:

184

Вы можете использовать это свойство Type.IsPrimitive, но будьте осторожны, потому что есть некоторые типы, которые мы можем рассматривать как примитивы, но это не так, например, Decimalи String.

Редактировать 1: Добавлен пример кода

Вот пример кода:

if (t.IsPrimitive || t == typeof(Decimal) || t == typeof(String) || ... )
{
    // Is Primitive, or Decimal, or String
}

Изменить 2: Как комментарии @SLaks , есть и другие типы, которые, возможно, вы также хотите рассматривать как примитивы. Я думаю, что вы должны будете добавить эти варианты один за другим .

Изменить 3: IsPrimitive = (логическое значение, байт, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double и Single), тип, аналогичный пыльниковому примитиву, для проверки (t == typeof (DateTime) ))

Хавьер
источник
12
И , возможно DateTime, TimeSpanи DateTimeOffset.
SLaks
Мммм ... да, вы правы. Я думаю, что мы должны добавить еще несколько возможностей
Хавьер
2
Вам нужно использовать логическое или ( ||), а не побитовое или ( |).
SLaks
42
Вот метод расширения, который я написал для удобного запуска тестов, описанных в ответах @Javier и Michael Petito : gist.github.com/3330614 .
Джонатан
5
Вы можете использовать свойство Type.IsValueType и добавить только проверку для строки.
Маттео Мильоре
58

Я только нашел этот вопрос, когда искал похожее решение, и подумал, что вас может заинтересовать следующий подход с использованием System.TypeCodeи System.Convert.

Легко сериализовать любой тип, который сопоставлен с System.TypeCodeдругим System.TypeCode.Object, поэтому вы можете сделать:

object PropertyValue = ...
if(Convert.GetTypeCode(PropertyValue) != TypeCode.Object)
{
    string StringValue = Convert.ToString(PropertyValue);
    ...
}

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

Майкл Петито
источник
2
Это здорово, мне приходилось добавлять вручную Guidдля собственных целей (как примитив в моем определении).
Эрик Филипс
56

Мы делаем это так в нашем ORM:

Type t;
bool isPrimitiveType = t.IsPrimitive || t.IsValueType || (t == typeof(string));

Я знаю, что использование IsValueTypeне лучший вариант (у вас могут быть свои очень сложные структуры), но он работает в 99% случаев (включая Nullables).

Alex
источник
6
Зачем вам IsPrimitive, если вы используете IsValueType? Разве не все примитивы имеют тип значения?
JoelFan
5
@JoelFan десятичный тип имеет значение IsPrimitive false, но IsValueType true
xhafan
3
@xhafan: Вы отвечаете не на тот вопрос. Все структуры подобны decimalв этом отношении. Но есть ли тип, для которого IsPrimitiveвозвращается, trueно IsValueTypeвозвращается false? Если такого типа нет, тогда t.IsPrimitiveпроверка не нужна.
Ли
6
@Lii, вы правы, каждый тип примитива имеет IsValueTypeзначение true, поэтому проверка IsPrimitiveне нужна. Ура!
xhafan
1
@ Veverke Они не делают. Вы можете иметь не примитивный тип значения, в этом случае свойства имеют разные значения.
Майкл Петито
38

Из ответа @Ronnie Overby и комментария @jonathanconway я написал этот метод, который работает для Nullable и исключает пользовательские структуры.

public static bool IsSimpleType(Type type)
{
    return
        type.IsPrimitive ||
        new Type[] {
            typeof(string),
            typeof(decimal),
            typeof(DateTime),
            typeof(DateTimeOffset),
            typeof(TimeSpan),
            typeof(Guid)
        }.Contains(type) ||
        type.IsEnum ||
        Convert.GetTypeCode(type) != TypeCode.Object ||
        (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && IsSimpleType(type.GetGenericArguments()[0]))
        ;
}

Со следующим TestCase:

struct TestStruct
{
    public string Prop1;
    public int Prop2;
}

class TestClass1
{
    public string Prop1;
    public int Prop2;
}

enum TestEnum { TheValue }

[Test]
public void Test1()
{
    Assert.IsTrue(IsSimpleType(typeof(TestEnum)));
    Assert.IsTrue(IsSimpleType(typeof(string)));
    Assert.IsTrue(IsSimpleType(typeof(char)));
    Assert.IsTrue(IsSimpleType(typeof(Guid)));

    Assert.IsTrue(IsSimpleType(typeof(bool)));
    Assert.IsTrue(IsSimpleType(typeof(byte)));
    Assert.IsTrue(IsSimpleType(typeof(short)));
    Assert.IsTrue(IsSimpleType(typeof(int)));
    Assert.IsTrue(IsSimpleType(typeof(long)));
    Assert.IsTrue(IsSimpleType(typeof(float)));
    Assert.IsTrue(IsSimpleType(typeof(double)));
    Assert.IsTrue(IsSimpleType(typeof(decimal)));

    Assert.IsTrue(IsSimpleType(typeof(sbyte)));
    Assert.IsTrue(IsSimpleType(typeof(ushort)));
    Assert.IsTrue(IsSimpleType(typeof(uint)));
    Assert.IsTrue(IsSimpleType(typeof(ulong)));

    Assert.IsTrue(IsSimpleType(typeof(DateTime)));
    Assert.IsTrue(IsSimpleType(typeof(DateTimeOffset)));
    Assert.IsTrue(IsSimpleType(typeof(TimeSpan)));

    Assert.IsFalse(IsSimpleType(typeof(TestStruct)));
    Assert.IsFalse(IsSimpleType(typeof(TestClass1)));

    Assert.IsTrue(IsSimpleType(typeof(TestEnum?)));
    Assert.IsTrue(IsSimpleType(typeof(char?)));
    Assert.IsTrue(IsSimpleType(typeof(Guid?)));

    Assert.IsTrue(IsSimpleType(typeof(bool?)));
    Assert.IsTrue(IsSimpleType(typeof(byte?)));
    Assert.IsTrue(IsSimpleType(typeof(short?)));
    Assert.IsTrue(IsSimpleType(typeof(int?)));
    Assert.IsTrue(IsSimpleType(typeof(long?)));
    Assert.IsTrue(IsSimpleType(typeof(float?)));
    Assert.IsTrue(IsSimpleType(typeof(double?)));
    Assert.IsTrue(IsSimpleType(typeof(decimal?)));

    Assert.IsTrue(IsSimpleType(typeof(sbyte?)));
    Assert.IsTrue(IsSimpleType(typeof(ushort?)));
    Assert.IsTrue(IsSimpleType(typeof(uint?)));
    Assert.IsTrue(IsSimpleType(typeof(ulong?)));

    Assert.IsTrue(IsSimpleType(typeof(DateTime?)));
    Assert.IsTrue(IsSimpleType(typeof(DateTimeOffset?)));
    Assert.IsTrue(IsSimpleType(typeof(TimeSpan?)));

    Assert.IsFalse(IsSimpleType(typeof(TestStruct?)));
}
Xav987
источник
1
Это хороший подход, но Enumон не поддерживается, протестируйте его enum MyEnum { EnumValue }и используйте MyEnum. @Jonathan также использует type.IsValueType. При том, что Enumsони правильно обнаружены, но и Structs. Так что следите, какие примитивы вы хотите.
Апфелкуача
1
@Apfelkuacha: вы совершенно правы. Но вместо того type.IsValueType, чтобы использовать , почему бы просто не добавить type.IsEnum?
Xav987
ты совершенно прав. type.IsEnumтоже возможно. Я предложил изменить ваше сообщение :)
Apfelkuacha
16

Вот как я это сделал.

   static class PrimitiveTypes
   {
       public static readonly Type[] List;

       static PrimitiveTypes()
       {
           var types = new[]
                          {
                              typeof (Enum),
                              typeof (String),
                              typeof (Char),
                              typeof (Guid),

                              typeof (Boolean),
                              typeof (Byte),
                              typeof (Int16),
                              typeof (Int32),
                              typeof (Int64),
                              typeof (Single),
                              typeof (Double),
                              typeof (Decimal),

                              typeof (SByte),
                              typeof (UInt16),
                              typeof (UInt32),
                              typeof (UInt64),

                              typeof (DateTime),
                              typeof (DateTimeOffset),
                              typeof (TimeSpan),
                          };


           var nullTypes = from t in types
                           where t.IsValueType
                           select typeof (Nullable<>).MakeGenericType(t);

           List = types.Concat(nullTypes).ToArray();
       }

       public static bool Test(Type type)
       {
           if (List.Any(x => x.IsAssignableFrom(type)))
               return true;

           var nut = Nullable.GetUnderlyingType(type);
           return nut != null && nut.IsEnum;
       }
   }
Ронни Оверби
источник
@RonnieOverby. Есть ли какая-то особенная причина, которую вы используете IsAssignableFromв своем тесте вместо содержимого?
Джонни 5
6

Также хорошая возможность:

private static bool IsPrimitiveType(Type type)
{
    return (type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object);
}
k3flo
источник
Каждый экземпляр Typeимеет свойство с именем IsPrimitive . Вы должны использовать это вместо этого.
Ренан
3
Ни примитивы, Stringни Decimalпримитивы.
k3flo
Это работает для меня, но я переименовал в IsClrType, чтобы не путать его значение с существующим .IsPrimitive для класса Type
KnarfaLingus
1
Это не выберет Guid или TimeSpan, например.
Станислав
3

Предполагая, что у вас есть подпись функции, подобная этой:

void foo<T>() 

Вы можете добавить общее ограничение, чтобы разрешить только типы значений:

void foo<T>() where T : struct

Обратите внимание, что это допускает не только примитивные типы для T, но и любой тип значения.

eWolf
источник
2

Мне нужно было сериализовать типы для экспорта в XML. Для этого я перебрал объект и выбрал поля, которые были примитивами, перечислениями, типами значений или сериализуемыми. Это был результат моего запроса:

Type contextType = context.GetType();

var props = (from property in contextType.GetProperties()
                         let name = property.Name
                         let type = property.PropertyType
                         let value = property.GetValue(context,
                                     (BindingFlags.GetProperty | BindingFlags.GetField | BindingFlags.Public),
                                     null, null, null)
                         where (type.IsPrimitive || type.IsEnum || type.IsValueType || type.IsSerializable)
                         select new { Name = name, Value = value});

Я использовал LINQ, чтобы перебрать типы, а затем получить их имя и значение для хранения в таблице символов. Ключ в предложении «где», которое я выбрал для размышления. Я выбрал примитивные, перечислимые, типы значений и сериализуемые типы. Это позволило проходить строки и объекты DateTime, как я и ожидал.

Ура!

JFalcon
источник
1

Это то, что у меня есть в моей библиотеке. Комментарии приветствуются.

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

  Public Shared Function IsPersistable(Type As System.Type) As Boolean
    With TypeInformation.UnderlyingType(Type)
      Return .IsValueType OrElse Type = GetType(String) OrElse .IsPrimitive
    End With
  End Function

  Public Shared Function IsNullable(ByVal Type As System.Type) As Boolean
    Return (Type.IsGenericType) AndAlso (Type.GetGenericTypeDefinition() Is GetType(Nullable(Of )))
  End Function

  Public Shared Function UnderlyingType(ByVal Type As System.Type) As System.Type
    If IsNullable(Type) Then
      Return Nullable.GetUnderlyingType(Type)
    Else
      Return Type
    End If
  End Function

Тогда я могу использовать это так:

  Public Shared Function PersistableProperties(Item As System.Type) As IEnumerable(Of System.Reflection.PropertyInfo)
    Return From PropertyInfo In Item.GetProperties()
                     Where PropertyInfo.CanWrite AndAlso (IsPersistable(PropertyInfo.PropertyType))
                     Select PropertyInfo
  End Function
toddmo
источник
0

Я просто хочу поделиться своим решением. Возможно, это полезно для всех.

public static bool IsPrimitiveType(Type fieldType)
{
   return fieldType.IsPrimitive || fieldType.Namespace.Equals("System");
}
Bahamut
источник
5
IsPrimitiveType(typeof(System.AccessViolationException)) == true
Ронни Оверби
2
namespace System { class MyNonPrimitiveType { } }
Ронни Оверби
0
public static bool IsPrimitiveType(object myObject)
{
   var myType = myObject.GetType();
   return myType.IsPrimitive || myType.Namespace == null ||  myType.Namespace.Equals("System");
}

Не забудьте проверить NULL пространство имен, потому что анонимные объекты не имеют назначенного пространства имен

iDusko
источник
0

Вот еще один жизнеспособный вариант.

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
user2023116
источник