Come verificare se il tipo è primitivo


162

Ho un blocco di codice che serializza un tipo in un tag 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);
}

Questa grande opera, tranne che voglio fare solo questo per i tipi primitivi, come int, double, boolecc, e di altri tipi che non sono primitive, ma può essere serializzato facilmente come string. Voglio che ignori tutto il resto come Elenchi e altri tipi personalizzati.

Qualcuno può suggerire come faccio? O devo specificare i tipi che voglio consentire da qualche parte e attivare il tipo di proprietà per vedere se è consentito? È un po 'disordinato, quindi sarebbe bello se ci fosse un modo più ordinato.


12
System.Stringnon è un tipo primitivo.
SL

3
Il modo migliore per farlo è di non usare affatto i generici. Se si supporta un numero limitato di tipi come tipi di parametri legali, è sufficiente che siano presenti molti sovraccarichi. Se si supporta qualsiasi tipo che implementa ISerializable, quindi scrivere un metodo non generico che accetta un ISerializable. Usa generici per cose che sono in realtà generiche ; se il tipo conta davvero, probabilmente non è generico.
Eric Lippert,

@Eric: Grazie, mi chiedo anche se puoi usare gli stessi criteri con i numeri? Ad esempio, per scrivere funzioni matematiche che supportano tutti i tipi numerici, ad esempio Media, Somma, ecc. Dovrebbero essere implementati usando Generic o overload? Importa se l'implementazione è la stessa o no? Perché è praticamente la stessa operazione per Media, Somma per qualsiasi tipo numerico, giusto?
Joan Venge,

1
@Joan: Essere in grado di scrivere metodi aritmetici generici su tipi vincolati per implementare vari operatori è una caratteristica spesso richiesta, ma richiede il supporto CLR ed è sorprendentemente complicata. Lo stiamo prendendo in considerazione per le versioni future della lingua, ma nessuna promessa.
Eric Lippert,

Risposte:


184

Puoi usare la proprietà Type.IsPrimitive, ma fai attenzione perché ci sono alcuni tipi che possiamo pensare che siano primitivi, ma non lo sono, per esempio Decimale String.

Modifica 1: aggiunto codice di esempio

Ecco un codice di esempio:

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

Modifica 2: Come commenta @SLaks , ci sono altri tipi che potresti voler trattare anche come primitivi. Penso che dovrai aggiungere queste variazioni una per una .

Modifica 3: IsPrimitive = (Booleano, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double e Single), Anther Tipo di tipo primitivo da controllare (t == typeof (DateTime ))


12
E forse DateTime, TimeSpane DateTimeOffset.
SL

Mmmm ... sì, hai ragione. Penso che dovremo aggiungere altre possibilità
Javier

2
È necessario utilizzare il logico o ( ||), non bit per bit o ( |).
SL

42
Ecco un metodo di estensione che ho scritto per eseguire comodamente i test descritti nelle risposte di @Javier e Michael Petito: gist.github.com/3330614 .
Jonathan,

5
È possibile utilizzare la proprietà Type.IsValueType e aggiungere solo il controllo per la stringa.
Matteo Migliore,

57

Ho appena trovato questa domanda mentre cercavo una soluzione simile e ho pensato che potresti essere interessato al seguente approccio usando System.TypeCodee System.Convert.

È facile serializzare qualsiasi tipo mappato su un System.TypeCodealtro System.TypeCode.Object, quindi puoi fare:

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

Il vantaggio di questo approccio è che non è necessario nominare ogni altro tipo non primitivo accettabile. È inoltre possibile modificare leggermente il codice sopra per gestire qualsiasi tipo che implementa IConvertible.


2
Questo è fantastico, ho dovuto aggiungere manualmente Guidper i miei scopi (come primitivo nella mia definizione).
Erik Philips,

56

Lo facciamo nel nostro ORM:

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

So che usare IsValueTypenon è l'opzione migliore (puoi avere le tue strutture molto complesse) ma funziona nel 99% dei casi (e include Nullables).


6
Perché è necessario IsPrimitive se si utilizza IsValueType? Non sono tutti i tipi di valore delle primitive?
JoelFan,

5
Il tipo decimale di @JoelFan ha IsPrimitive false, ma IsValueType true
xhafan

3
@xhafan: rispondi alla domanda sbagliata. Tutte le strutture sono come decimalal riguardo. Ma esiste un tipo per cui IsPrimitiveritorna truema IsValueTyperitorna false? Se non esiste tale tipo, il t.IsPrimitivetest non è necessario.
Lii,

6
@Lii hai ragione, ogni tipo di primitiva è IsValueTypeimpostato su true, quindi IsPrimitivenon è necessario verificare. Saluti!
Xhafan,

1
@Veverke Non lo fanno. È possibile avere un tipo di valore non primitivo, nel qual caso le proprietà hanno valori diversi.
Michael Petito,

38

Dalla risposta di @Ronnie Overby e dal commento di @jonathanconway, ho scritto questo metodo che funziona per Nullable ed escludo le strutture dell'utente.

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

Con il seguente 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
Questo è un buon approccio, ma Enumnon è supportato, testalo conenum MyEnum { EnumValue } e usando MyEnum. @Jonathan sta anche usando type.IsValueType. Detto questo Enumsvengono rilevati correttamente, ma anche Structs. Quindi fai attenzione a quali primitivi vuoi.
Apfelkuacha,

1
@Apfelkuacha: hai perfettamente ragione. Ma invece usandotype.IsValueType , perché semplicemente non aggiungere type.IsEnum?
Xav987,

hai perfettamente ragione. type.IsEnumè anche possibile. Ho suggerito una modifica sul tuo post :)
Apfelkuacha,

16

Ecco come l'ho fatto.

   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. c'è qualche motivo paticolare che usi IsAssignableFromnel tuo test invece di contenere?
johnny 5

6

Anche una buona possibilità:

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

Ogni istanza di Typeha una proprietà chiamata IsPrimitive . Dovresti usarlo invece.
Renan,

3
StringDecimalsono primitive.
k3flo,

Questo funziona per me, ma ho rinominato IsClrType per non confondere il suo significato con l'attuale .IsPrimitive sulla classe Type
KnarfaLingus,

1
Questo non sceglierà Guid o TimeSpan, per esempio.
Stanislav,

3

Supponendo che tu abbia una firma di funzione come questa:

void foo<T>() 

È possibile aggiungere un vincolo generico per consentire solo i tipi di valore:

void foo<T>() where T : struct

Si noti che ciò consente non solo tipi primitivi per T, ma qualsiasi tipo di valore.


2

Avevo bisogno di serializzare i tipi allo scopo di esportarli in XML. Per fare ciò, ho ripetuto l'oggetto e ho optato per campi che erano primitivi, enum, tipi di valore o serializzabili. Questo è stato il risultato della mia domanda:

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

Ho usato LINQ per scorrere i tipi, quindi ottenere il loro nome e valore da memorizzare in una tabella dei simboli. La chiave sta nella clausola "dove" che ho scelto per la riflessione. Ho scelto tipi primitivi, enumerati, di valore e tipi serializzabili. Ciò ha consentito alle stringhe e agli oggetti DateTime di passare come previsto.

Saluti!


1

Questo è quello che ho nella mia biblioteca. I commenti sono ben accetti

Controllo prima IsValueType, poiché gestisce la maggior parte dei tipi, quindi String, poiché è il secondo più comune. Non riesco a pensare a una primitiva che non sia un tipo di valore, quindi non so se quella gamba del se mai viene colpita.

  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

Quindi posso usarlo in questo modo:

  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

Voglio solo condividere la mia soluzione. Forse è utile a chiunque.

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

Non dimenticare di controllare lo spazio dei nomi NULL, perché agli oggetti anonimi non è stato assegnato lo spazio dei nomi


0

Ecco un'altra opzione praticabile.

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.