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.


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


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.


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).


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

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

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

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.


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!


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

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

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


0

Aquí hay otra opción viable.

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.