Cómo probar si el tipo es primitivo

162

Tengo un bloque de código que serializa un tipo en una etiqueta 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);
}

Esto funciona muy bien, excepto lo quiero sólo para hacer esto para los tipos primitivos, como int, double, booletc., y otros tipos que no son primitivos, pero se puede serializar fácilmente como string. Quiero que ignore todo lo demás, como listas y otros tipos personalizados.

¿Alguien puede sugerir cómo hago esto? ¿O necesito especificar los tipos que quiero permitir en algún lugar y activar el tipo de propiedad para ver si está permitido? Eso es un poco desordenado, por lo que sería bueno si hubiera una forma más ordenada.

DaveDev
fuente
12
System.StringNo es un tipo primitivo.
SLaks
3
La mejor manera de hacerlo es no usar genéricos en absoluto. Si admite una pequeña cantidad de tipos como tipos de parámetros legales, simplemente tenga esa sobrecarga. Si admite cualquier tipo que implemente ISerializable, escriba un método no genérico que tome un ISerializable. Use genéricos para cosas que en realidad son genéricas ; si el tipo realmente importa, probablemente no sea genérico.
Eric Lippert
@Eric: Gracias, también me pregunto si puedes usar el mismo criterio con los números. Por ejemplo, para escribir funciones matemáticas que admitan todos los tipos numéricos, es decir, Promedio, Suma, etc. ¿Deberían implementarse usando Genéricos o sobrecargas? ¿Importa si la implementación es la misma o no? Porque es más o menos la misma operación para Promedio, Suma para cualquier tipo numérico, ¿verdad?
Joan Venge
1
@Joan: Poder escribir métodos aritméticos genéricos en tipos restringidos para implementar varios operadores es una característica solicitada con frecuencia, pero requiere soporte de CLR y es sorprendentemente complicado. Lo estamos considerando para futuras versiones del lenguaje, pero no hay promesas.
Eric Lippert

Respuestas:

184

Puede usar la propiedad Type.IsPrimitive, pero tenga cuidado porque hay algunos tipos que podemos pensar que son primitivos, pero no lo son, por ejemplo, Decimaly String.

Edición 1: código de muestra agregado

Aquí hay un código de muestra:

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

Edición 2: Como comenta @SLaks , hay otros tipos que quizás también quieras tratar como primitivos. Creo que tendrás que agregar estas variaciones una por una .

Edición 3: IsPrimitive = (Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double y Single), tipo Anther Primitive-Like para comprobar (t == typeof (DateTime ))

Javier
fuente
12
Y tal vez DateTime, TimeSpany DateTimeOffset.
SLaks
Mmmm ... sí, tienes razón. Creo que tendremos que agregar algunas posibilidades más
Javier
2
Necesita usar el lógico o ( ||), no el bit a bit o ( |).
SLaks
42
Aquí hay un método de extensión que escribí para ejecutar convenientemente las pruebas descritas en las respuestas de @Javier y Michael Petito: gist.github.com/3330614 .
Jonathan
55
Puede usar la propiedad Type.IsValueType y agregar solo la comprobación de la cadena.
Matteo Migliore
57

Acabo de encontrar esta pregunta mientras buscaba una solución similar, y pensé que podría estar interesado en el siguiente enfoque usando System.TypeCodey System.Convert.

Es fácil serializar cualquier tipo que esté asignado a System.TypeCodeotro que no sea System.TypeCode.Object, por lo que podría hacer:

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

La ventaja de este enfoque es que no tiene que nombrar cualquier otro tipo no primitivo aceptable. También puede modificar ligeramente el código anterior para manejar cualquier tipo que implemente IConvertible.

Michael Petito
fuente
2
Esto es genial, tuve que agregar manualmente Guidpara mis propios fines (como primitivo en mi definición).
Erik Philips
56

Lo hacemos así en nuestro ORM:

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

Sé que usar IsValueTypeno es la mejor opción (puede tener sus propias estructuras muy complejas) pero funciona en 99% de los casos (e incluye Nullables).

Alex
fuente
66
¿Por qué necesita IsPrimitive si está utilizando IsValueType? ¿No son todos los tipos de valores primitivos?
JoelFan
55
El tipo decimal @JoelFan tiene IsPrimitive falso, pero IsValueType verdadero
xhafan
3
@xhafan: Respondes la pregunta equivocada. Todas las estructuras son como decimalen ese sentido. Pero, ¿hay algún tipo para el que IsPrimitiveregrese truepero IsValueTyperegrese false? Si no existe tal tipo, entonces la t.IsPrimitiveprueba es innecesaria.
Lii
66
@Lii tienes razón, cada tipo primitivo se ha IsValueTypeestablecido en verdadero, por IsPrimitivelo que no es necesario verificarlo . ¡Salud!
xhafan
1
@Veverke No lo hacen. Puede tener un tipo de valor no primitivo, en cuyo caso las propiedades tienen valores diferentes.
Michael Petito
38

Desde la respuesta de @Ronnie Overby y el comentario de @jonathanconway, escribí este método que funciona para Nullable y excluyo las estructuras de usuario.

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 el siguiente 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
fuente
1
Este es un buen enfoque, pero Enumno es compatible, pruébelo enum MyEnum { EnumValue }y utilícelo MyEnum. @Jonathan también está usando type.IsValueType. Con eso Enumsse detectan correctamente, pero también Structs. Así que ten cuidado con las primitivas que quieres.
Apfelkuacha
1
@Apfelkuacha: tienes toda la razón. Pero en lugar de usar type.IsValueType, ¿por qué simplemente no agregar type.IsEnum?
Xav987
tienes toda la razón. type.IsEnumTambién es posible. He sugerido una edición en tu publicación :)
Apfelkuacha
16

Así es como lo hice.

   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
fuente
@RonnieOverby. ¿Hay alguna razón especial que use IsAssignableFromen su prueba en lugar de contener?
Johnny 5
6

También una buena posibilidad:

private static bool IsPrimitiveType(Type type)
{
    return (type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object);
}
k3flo
fuente
Cada instancia de Typetiene una propiedad llamada IsPrimitive . Deberías usar eso en su lugar.
Renan
3
Ni Stringtampoco lo Decimalson los primitivos.
k3flo 19/0613
Esto funciona para mí, pero cambié el nombre a IsClrType para no confundir su significado con el .IsPrimitive existente en la clase Type
KnarfaLingus
1
Esto no elegirá Guid o TimeSpan, por ejemplo.
Stanislav
3

Suponiendo que tiene una firma de función como esta:

void foo<T>() 

Puede agregar una restricción genérica para permitir solo tipos de valores:

void foo<T>() where T : struct

Tenga en cuenta que esto no solo permite tipos primitivos para T, sino también cualquier tipo de valor.

eWolf
fuente
2

Tenía la necesidad de serializar tipos para exportarlos a XML. Para hacer esto, realicé una iteración a través del objeto y opté por campos que eran primitivos, enumeración, tipos de valor o serializables. Este fue el resultado de mi consulta:

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

Usé LINQ para recorrer los tipos y luego obtener su nombre y valor para almacenarlos en una tabla de símbolos. La clave está en la cláusula 'dónde' que elegí para la reflexión. Elegí tipos primitivos, enumerados, de valor y tipos serializables. Esto permitió que las cadenas y los objetos DateTime aparecieran como esperaba.

¡Salud!

JFalcon
fuente
1

Esto es lo que tengo en mi biblioteca. Comentarios son bienvenidos

Primero verifico IsValueType, ya que maneja la mayoría de los tipos, luego String, ya que es el segundo más común. No puedo pensar en una primitiva que no sea un tipo de valor, por lo que no sé si esa parte del si alguna vez es golpeada.

  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

Entonces puedo usarlo así:

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

Solo quiero compartir mi solución. Quizás sea útil para cualquiera.

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

No olvide verificar el espacio de nombres NULL, porque los objetos anónimos no tienen espacio de nombres asignado

iDusko
fuente
0

Aquí hay otra opción viable.

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