Як перевірити, чи тип примітивний


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. Я хочу, щоб вона ігнорувала все інше, як списки та інші користувацькі типи.

Хтось може підказати, як я це роблю? Або мені потрібно вказати типи, які я хочу десь дозволити, і ввімкнути тип властивості, щоб побачити, чи це дозволено? Це трохи безладно, тому було б непогано, якби я був там охайнішим способом.


12
System.Stringне є примітивним типом.
СЛАкс

3
Кращий спосіб зробити це - взагалі не використовувати дженерики. Якщо ви підтримуєте невелику кількість типів як легальні типи параметрів, то просто так багато перевантажень. Якщо ви підтримуєте будь-який тип, який реалізує ISerializable, тоді напишіть негенеричний метод, який приймає ISerializable. Використовуйте дженерики для речей, які насправді є загальними ; якщо тип насправді має значення, він, ймовірно, не є загальним.
Ерік Ліпперт

@Eric: Спасибі, мені також цікаво, чи можна використовувати ті ж критерії, що й числові цифри? Наприклад, писати математичні функції, які підтримують всі числові типи, наприклад, середнє значення, сума тощо. Чи повинні вони бути реалізовані за допомогою загальних або перевантажень? Чи має значення те, чи реалізація однакова чи ні? Тому що це майже однакова операція для "Середня", "Сума" для будь-якого числового типу, правда?
Джоан Венге

1
@Joan: Вміти писати загальні арифметичні методи для типів, обмежених для реалізації різних операторів, є часто запитуваною функцією, але вона вимагає підтримки CLR і напрочуд складна. Ми розглядаємо це для майбутніх версій мови, але жодних обіцянок.
Ерік Ліпперт

Відповіді:


184

Ви можете використовувати властивість Type.IsPrimitive, але будьте обережні , тому що є деякі типи , які ми можемо думати , що примітиви, але вони are not, наприклад , Decimalі String.

Редагувати 1: Доданий зразок коду

Ось зразок коду:

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

Редагувати 2: Як зазначає @SLaks , є й інші типи, які, можливо, ви теж хочете вважати примітивами. Я думаю, що вам доведеться додавати ці варіанти по черзі .

Редагувати 3: IsPrimitive = (Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double та Single), тип Anther Primitive-Like для перевірки (t == typeof (DateTime ))


12
І , можливо DateTime, TimeSpanі DateTimeOffset.
СЛАкс

Мммм ... так, ти маєш рацію. Я думаю, що нам доведеться додати ще кілька можливостей
Хав'єр,

2
Вам потрібно використовувати логічне або ( ||), а не порозрядне або ( |).
СЛАкс

42
Ось метод розширення, який я написав, щоб зручно запустити тести, описані у відповідях @Javier та Michael Petito: gist.github.com/3330614 .
Джонатан

5
Ви можете використовувати властивість Type.IsValueType і додати лише чек для рядка.
Маттео Мільйоре

57

Я щойно знайшов це питання, шукаючи подібне рішення, і подумав, що вас може зацікавити наступний підхід із використанням 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).


6
Для чого потрібен IsPrimitive, якщо ви використовуєте IsValueType? Чи не всі значення примітивів значення?
JoelFan

5
Десятковий тип @JoelFan має помилковий IsPrimitive, але правда
IsValueType

3
@xhafan: Ви відповідаєте на неправильне запитання. Усі структури подібні decimalдо цього. Але чи є тип, для якого IsPrimitiveповертається, trueале IsValueTypeповертається false? Якщо такого типу немає, то t.IsPrimitiveтест зайвий.
Лій

6
@Lii ви маєте рацію, кожен примітивний тип IsValueTypeвстановив істину, тому перевіряти 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?)));
}

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у своєму тесті замість містить?
johnny 5

6

Також хороша можливість:

private static bool IsPrimitiveType(Type type)
{
    return (type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object);
}

Кожен екземпляр Typeмає властивість під назвою IsPrimitive . Ви повинні використовувати це замість цього.
Ренан

3
Ні, Stringні Decimalпримітиви.
k3flo

Це працює для мене, але я перейменував його на IsClrType, щоб не плутати його значення з існуючим .IsPrimitive для класу Type
KnarfaLingus,

1
Наприклад, це не вибере Guid або TimeSpan.
Станіслав

3

Якщо припустимо, що у вас є такий підпис функції:

void foo<T>() 

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

void foo<T>() where T : struct

Зауважте, що це дозволяє не тільки примітивні типи для T, але й будь-який тип значення.


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 проходити так, як я очікував.

Ура!


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

0

Я просто хочу поділитися своїм рішенням. Можливо, комусь це корисно.

public static bool IsPrimitiveType(Type fieldType)
{
   return fieldType.IsPrimitive || fieldType.Namespace.Equals("System");
}

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, оскільки анонімні об'єкти не мають призначеного простору імен


0

Ось ще один життєздатний варіант.

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.