Jak sprawdzić, czy typ jest prymitywny


162

Mam blok kodu, który serializuje typ do tagu 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);
}

Działa to doskonale, z wyjątkiem chcę, żeby to zrobić tylko dla typów pierwotnych, jak int, double, boolitp, i inne typy, które nie są prymitywne, ale może być łatwo jak serializacjistring . Chcę, aby ignorował wszystko inne, takie jak listy i inne typy niestandardowe.

Czy ktoś może zasugerować, jak to zrobić? A może muszę określić typy, na które chcę zezwolić, i włączyć typ właściwości, aby sprawdzić, czy jest dozwolony? To trochę bałaganiarskie, więc byłoby miło, gdybym był bardziej uporządkowany.


12
System.Stringnie jest typem pierwotnym.
SLaks

3
Lepszym sposobem na to jest całkowite zaniechanie używania leków generycznych. Jeśli obsługujesz niewielką liczbę typów jako dozwolone typy parametrów, po prostu miej tyle przeciążeń. Jeśli obsługujesz dowolny typ, który implementuje ISerializable, napisz metodę inną niż ogólna, która przyjmuje ISerializable. Używaj typów ogólnych do rzeczy, które są faktycznie ogólne ; jeśli typ ma znaczenie, prawdopodobnie nie jest ogólny.
Eric Lippert

@Eric: Dzięki. Zastanawiam się też, czy można zastosować te same kryteria w przypadku liczb? Na przykład, aby napisać funkcje matematyczne, które obsługują wszystkie typy liczbowe, tj. Średnią, sumę, itp. Czy powinny być implementowane przy użyciu opcji Generic czy przeciążenia? Czy ma znaczenie, czy implementacja jest taka sama, czy nie? Ponieważ jest to prawie taka sama operacja dla średniej, sumy dla dowolnego typu liczbowego, prawda?
Joan Venge

1
@Joan: Możliwość pisania ogólnych metod arytmetycznych na typach ograniczonych do implementacji różnych operatorów jest często żądaną funkcją, ale wymaga obsługi CLR i jest zaskakująco skomplikowana. Rozważamy to w przyszłych wersjach języka, ale bez obietnic.
Eric Lippert

Odpowiedzi:


184

Możesz użyć tej właściwości Type.IsPrimitive, ale zachowaj ostrożność, ponieważ są pewne typy, które możemy uważać za prymitywne, ale tak nie jest, na przykład Decimali String.

Edycja 1: Dodano przykładowy kod

Oto przykładowy kod:

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

Edycja 2: As @SLaks komentuje , istnieją inne typy, które być może chcesz również traktować jako prymitywy. Myślę, że będziesz musiał dodawać te odmiany jeden po drugim .

Edycja 3: IsPrimitive = (Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double i Single), Anther Primitive-Like type to check (t == typeof (DateTime ))


12
I być może DateTime, TimeSpani DateTimeOffset.
SLaks

Mmmm ... tak, masz rację. Myślę, że będziemy musieli dodać więcej możliwości
Javier

2
Musisz użyć logicznego lub ( ||), a nie bitowego lub ( |).
SLaks

42
Oto metoda rozszerzenia, którą napisałem w celu wygodnego uruchamiania testów opisanych w odpowiedziach @Javier i Michael Petito: gist.github.com/3330614 .
Jonathan

5
Możesz użyć właściwości Type.IsValueType i dodać tylko sprawdzenie dla ciągu.
Matteo Migliore

57

Właśnie znalazłem to pytanie, szukając podobnego rozwiązania, i pomyślałem, że możesz być zainteresowany następującym podejściem przy użyciu System.TypeCodei System.Convert.

Łatwo jest serializować dowolny typ, który jest zamapowany na System.TypeCodeinny niż System.TypeCode.Object, więc możesz zrobić:

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

Zaletą tego podejścia jest to, że nie musisz nazywać każdego innego akceptowalnego typu niepierwotnego. Możesz również nieznacznie zmodyfikować powyższy kod, aby obsłużyć dowolny typ, który implementuje IConvertible.


2
To jest świetne, musiałem ręcznie dodać Guiddo własnych celów (jako prymityw w mojej definicji).
Erik Philips,

56

Robimy to w naszym ORM:

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

Wiem, że użycie IsValueTypenie jest najlepszą opcją (możesz mieć własne bardzo złożone struktury), ale działa w 99% przypadków (i obejmuje Nullables).


6
Dlaczego potrzebujesz IsPrimitive, jeśli używasz IsValueType? Czy nie wszystkie pierwotne typy wartości są?
JoelFan

5
Typ dziesiętny @JoelFan ma wartość IsPrimitive false, ale IsValueType true
xhafan

3
@xhafan: Odpowiadasz na złe pytanie. Pod decimaltym względem wszystkie struktury są podobne . Ale czy istnieje typ, dla którego IsPrimitivewraca, trueale IsValueTypewraca false? Jeśli nie ma takiego typu, t.IsPrimitivetest jest zbędny.
Lii

6
@Lii masz rację, każdy typ prymitywny ma IsValueTypeustawioną wartość true, więc sprawdzanie IsPrimitivenie jest konieczne. Twoje zdrowie!
xhafan

1
@Veverke Oni tego nie robią. Możesz mieć inny niż pierwotny typ wartości, w którym to przypadku właściwości mają różne wartości.
Michael Petito,

38

Z odpowiedzi @Ronnie Overby i komentarza @jonathanconway napisałem tę metodę, która działa dla Nullable i wyklucza struktury użytkownika.

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]))
        ;
}

Z następującym 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
Jest to dobre podejście, ale Enumnie jest obsługiwane, przetestuj je enum MyEnum { EnumValue }i używaj MyEnum. @Jonathan też używa type.IsValueType. Dzięki temu Enumssą poprawnie wykrywane, ale także Structs. Więc uważaj, jakich prymitywów chcesz.
Apfelkuacha

1
@Apfelkuacha: masz całkowitą rację. Ale zamiast tego type.IsValueType, dlaczego po prostu nie dodać type.IsEnum?
Xav987

masz całkowitą rację. type.IsEnumjest również możliwe. Zaproponowałem zmianę w Twoim poście :)
Apfelkuacha

16

Oto jak to zrobiłem.

   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. czy jest jakiś szczególny powód, dla którego używasz IsAssignableFromw swoim teście zamiast zawiera?
johnny 5

6

Również dobra możliwość:

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

Każde wystąpienie Typema właściwość o nazwie IsPrimitive . Zamiast tego powinieneś użyć tego.
Renan

3
Ani Stringteż nie Decimalsą prymitywami.
k3flo

To działa dla mnie, ale zmieniłem nazwę na IsClrType, aby nie mylić jego znaczenia z istniejącym .IsPrimitive w klasie Type
KnarfaLingus

1
Na przykład nie wybierze to Guid ani TimeSpan.
Stanislav

3

Zakładając, że masz taki podpis funkcji:

void foo<T>() 

Możesz dodać ogólne ograniczenie, aby zezwolić tylko na typy wartości:

void foo<T>() where T : struct

Zauważ, że pozwala to nie tylko na typy pierwotne dla T, ale także na dowolny typ wartości.


2

Musiałem serializować typy w celu wyeksportowania ich do XML. Aby to zrobić, przeszedłem przez obiekt i wybrałem pola, które były prymitywne, wyliczeniowe, typy wartości lub możliwe do serializacji. To był wynik mojego zapytania:

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});

Użyłem LINQ do iteracji po typach, a następnie uzyskałem ich nazwę i wartość do przechowywania w tabeli symboli. Kluczem jest klauzula „gdzie”, którą wybrałem do refleksji. Wybrałem typy prymitywne, wyliczeniowe, typy wartości i typy możliwe do serializacji. Pozwoliło to na przejście ciągów i obiektów DateTime zgodnie z oczekiwaniami.

Twoje zdrowie!


1

To jest to, co mam w mojej bibliotece. Komentarze są mile widziane.

Najpierw sprawdzam IsValueType, ponieważ obsługuje większość typów, a następnie String, ponieważ jest to drugi najpopularniejszy. Nie przychodzi mi do głowy prymityw, który nie jest typem wartości, więc nie wiem, czy ta noga, jeśli kiedykolwiek zostanie trafiona.

  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

Wtedy mogę tego użyć w ten sposób:

  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

Chcę tylko udostępnić moje rozwiązanie. Być może jest to przydatne dla każdego.

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

5
IsPrimitiveType(typeof(System.AccessViolationException)) == true
Ronnie Overby

2
namespace System { class MyNonPrimitiveType { } }
Ronnie Overby

0
public static bool IsPrimitiveType(object myObject)
{
   var myType = myObject.GetType();
   return myType.IsPrimitive || myType.Namespace == null ||  myType.Namespace.Equals("System");
}

Nie zapomnij sprawdzić przestrzeni nazw NULL, ponieważ obiekty anonimowe nie mają przypisanej przestrzeni nazw


0

Oto kolejna realna opcja.

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.