So testen Sie, ob der Typ primitiv ist

162

Ich habe einen Codeblock, der einen Typ in ein HTML-Tag serialisiert.

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

Dies funktioniert gut, außer ich will dies nur tun , für primitive Typen, wie int, double, boolusw., und andere Arten , die nicht primitiv sind , aber leicht wie serialisiert werden string. Ich möchte, dass alles andere wie Listen und andere benutzerdefinierte Typen ignoriert wird.

Kann mir jemand vorschlagen, wie ich das mache? Oder muss ich die Typen angeben, die ich irgendwo zulassen möchte, und den Typ der Eigenschaft aktivieren, um zu sehen, ob dies zulässig ist? Das ist ein bisschen chaotisch, also wäre es schön, wenn ich einen ordentlicheren Weg hätte.

DaveDev
quelle
12
System.Stringist kein primitiver Typ.
SLaks
3
Der bessere Weg, dies zu tun, besteht darin, überhaupt keine Generika zu verwenden. Wenn Sie eine kleine Anzahl von Typen als zulässige Parametertypen unterstützen, haben Sie einfach so viele Überladungen. Wenn Sie einen Typ unterstützen, der ISerializable implementiert, schreiben Sie eine nicht generische Methode, die ISerializable verwendet. Verwenden Sie Generika für Dinge, die tatsächlich generisch sind . Wenn der Typ wirklich wichtig ist, ist er wahrscheinlich nicht generisch.
Eric Lippert
@Eric: Danke, ich frage mich auch, ob Sie die gleichen Kriterien mit Zahlen verwenden können? Zum Beispiel, um mathematische Funktionen zu schreiben, die alle numerischen Typen unterstützen, dh Durchschnitt, Summe usw. Sollten sie mit Generic oder Überladungen implementiert werden? Ist es wichtig, ob die Implementierung dieselbe ist oder nicht? Weil es für Average, Sum für jeden numerischen Typ so ziemlich die gleiche Operation ist, oder?
Joan Venge
1
@Joan: Die Möglichkeit, generische arithmetische Methoden für Typen zu schreiben, die auf die Implementierung verschiedener Operatoren beschränkt sind, ist eine häufig nachgefragte Funktion, erfordert jedoch CLR-Unterstützung und ist überraschend kompliziert. Wir ziehen es für zukünftige Versionen der Sprache in Betracht, aber keine Versprechen.
Eric Lippert

Antworten:

182

Sie können die Eigenschaft verwenden Type.IsPrimitive, aber seien Sie vorsichtig, da es einige Typen gibt, von denen wir glauben können, dass sie primitiv sind, aber sie sind es nicht, zum Beispiel Decimalund String.

Bearbeiten 1: Beispielcode hinzugefügt

Hier ist ein Beispielcode:

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

Bearbeiten 2: Wie @SLaks kommentiert, gibt es andere Typen, die Sie möglicherweise auch als Grundelemente behandeln möchten. Ich denke , dass du wirst diese Variationen hinzuzufügen haben eins nach dem anderen .

Bearbeiten 3: IsPrimitive = (Boolescher Wert, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double und Single), Anther Primitive-Like-Typ zum Überprüfen (t == typeof (DateTime) ))

Javier
quelle
12
Und vielleicht DateTime, TimeSpanund DateTimeOffset.
SLaks
Mmmm ... ja, du hast recht. Ich denke, wir müssen noch einige Möglichkeiten hinzufügen
Javier
2
Sie müssen das logische oder ( ||) verwenden, nicht das bitweise oder ( |).
SLaks
42
Hier ist eine Erweiterungsmethode, die ich geschrieben habe, um die in den Antworten von @Javier und Michael Petito beschriebenen Tests bequem auszuführen: gist.github.com/3330614 .
Jonathan
5
Sie können die Eigenschaft Type.IsValueType verwenden und nur die Prüfung für die Zeichenfolge hinzufügen.
Matteo Migliore
57

Ich habe diese Frage gerade gefunden, als ich nach einer ähnlichen Lösung gesucht habe, und dachte, Sie könnten an dem folgenden Ansatz mit System.TypeCodeund interessiert sein System.Convert.

Es ist einfach, jeden Typ zu serialisieren, der einem System.TypeCodeanderen als zugeordnet System.TypeCode.Objectist. Sie können also Folgendes tun:

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

Der Vorteil dieses Ansatzes besteht darin, dass Sie nicht jeden anderen akzeptablen nicht-primitiven Typ benennen müssen. Sie können den obigen Code auch geringfügig ändern, um jeden Typ zu verarbeiten, der IConvertible implementiert.

Michael Petito
quelle
2
Das ist großartig, ich musste es manuell Guidfür meine eigenen Zwecke hinzufügen (als Grundelement in meiner Definition).
Erik Philips
56

Wir machen das in unserem ORM so:

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

Ich weiß, dass die Verwendung IsValueTypenicht die beste Option ist (Sie können Ihre eigenen sehr komplexen Strukturen haben), aber sie funktioniert in 99% der Fälle (und enthält Nullables).

Alex
quelle
6
Warum benötigen Sie IsPrimitive, wenn Sie IsValueType verwenden? Sind nicht alle Grundelemente Werttypen?
JoelFan
5
@ JoelFan Dezimaltyp hat IsPrimitive false, aber IsValueType true
xhafan
3
@xhafan: Du beantwortest die falsche Frage. Alle Strukturen sind decimalin dieser Hinsicht gleich. Aber gibt es einen Typ, für den IsPrimitivezurückgegeben wird, trueaber IsValueTypezurückgegeben wird false? Wenn es keinen solchen Typ gibt, ist der t.IsPrimitiveTest nicht erforderlich.
Lii
6
@Lii Sie haben Recht, jeder primitive Typ wurde IsValueTypeauf true gesetzt, sodass IsPrimitivekeine Überprüfung erforderlich ist. Prost!
Xhafan
1
@ Everke Sie nicht. Sie können einen nicht primitiven Wertetyp haben. In diesem Fall haben die Eigenschaften unterschiedliche Werte.
Michael Petito
38

Aus der Antwort von @Ronnie Overby und dem Kommentar von @jonathanconway habe ich diese Methode geschrieben, die für Nullable funktioniert und Benutzerstrukturen ausschließt.

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

Mit folgendem 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
quelle
1
Dies ist ein guter Ansatz, wird jedoch Enumnicht unterstützt. Testen Sie ihn mit enum MyEnum { EnumValue }und verwenden Sie ihn MyEnum. @ Jonathan verwendet auch type.IsValueType. Damit werden die Enumsaber auch richtig erkannt Structs. Achten Sie also darauf, welche Grundelemente Sie möchten.
Apfelkuacha
1
@ Apfelkuacha: Du hast vollkommen recht. Aber stattdessen verwenden type.IsValueType, warum nicht einfach hinzufügen type.IsEnum?
Xav987
Du hast vollkommen Recht. type.IsEnumist auch möglich. Ich habe eine Bearbeitung für Ihren Beitrag vorgeschlagen :)
Apfelkuacha
16

So habe ich es gemacht.

   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;
       }
   }
Ronnie Overby
quelle
@ RonnieOverby. Gibt es einen besonderen Grund, den Sie IsAssignableFromin Ihrem Test verwenden, anstatt zu enthalten?
Johnny 5.
6

Auch eine gute Möglichkeit:

private static bool IsPrimitiveType(Type type)
{
    return (type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object);
}
k3flo
quelle
Jede Instanz von Typehat eine Eigenschaft namens IsPrimitive . Sie sollten das stattdessen verwenden.
Renan
3
Weder Stringnoch Decimalsind Primitive.
k3flo
Dies funktioniert für mich, aber ich habe es in IsClrType umbenannt, um seine Bedeutung nicht mit dem vorhandenen .IsPrimitive für die Type-Klasse zu verwechseln
KnarfaLingus
1
Hiermit wird beispielsweise weder Guid noch TimeSpan ausgewählt.
Stanislav
3

Angenommen, Sie haben eine Funktionssignatur wie diese:

void foo<T>() 

Sie können eine generische Einschränkung hinzufügen, um nur Werttypen zuzulassen:

void foo<T>() where T : struct

Beachten Sie, dass dies nicht nur primitive Typen für T zulässt, sondern jeden Werttyp.

eWolf
quelle
2

Ich musste Typen serialisieren, um sie nach XML zu exportieren. Zu diesem Zweck habe ich das Objekt durchlaufen und mich für Felder entschieden, die primitiv, enum, Werttypen oder serialisierbar waren. Dies war das Ergebnis meiner Anfrage:

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

Ich habe LINQ verwendet, um die Typen zu durchlaufen und dann ihren Namen und Wert in einer Symboltabelle zu speichern. Der Schlüssel liegt in der 'where'-Klausel, die ich zum Nachdenken ausgewählt habe. Ich habe primitive, aufgezählte, Werttypen und serialisierbare Typen ausgewählt. Dadurch konnten Zeichenfolgen und DateTime-Objekte wie erwartet durchlaufen werden.

Prost!

JFalcon
quelle
1

Das habe ich in meiner Bibliothek. Kommentare sind willkommen.

Ich überprüfe zuerst IsValueType, da es die meisten Typen behandelt, dann String, da es das zweithäufigste ist. Ich kann mir kein Grundelement vorstellen, das kein Werttyp ist, also weiß ich nicht, ob dieses Bein des If jemals getroffen wird.

  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

Dann kann ich es so benutzen:

  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
quelle
0

Ich möchte nur meine Lösung teilen. Vielleicht ist es für jeden nützlich.

public static bool IsPrimitiveType(Type fieldType)
{
   return fieldType.IsPrimitive || fieldType.Namespace.Equals("System");
}
Bahamut
quelle
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");
}

Vergessen Sie nicht, den NULL-Namespace zu überprüfen, da anonymen Objekten kein Namespace zugewiesen wurde

iDusko
quelle
0

Hier ist eine weitere praktikable Option.

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
user2023116
quelle