Como testar se o tipo é primitivo

162

Eu tenho um bloco de código que serializa um tipo em uma marca de 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);
}

Isso funciona muito bem, exceto eu quero apenas fazer isso para tipos primitivos, como int, double, booletc, e outros tipos que não são primitivos, mas pode ser serializado facilmente como string. Quero que ele ignore todo o resto, como Listas e outros tipos personalizados.

Alguém pode sugerir como eu faço isso? Ou preciso especificar os tipos que quero permitir em algum lugar e ativar o tipo da propriedade para ver se é permitido? Isso é um pouco confuso, então seria bom se eu tivesse uma maneira mais organizada.

DaveDev
fonte
12
System.Stringnão é um tipo primitivo.
SLaks
3
A melhor maneira de fazer isso é não usar genéricos. Se você oferece suporte a um pequeno número de tipos como tipos de parâmetros legais, basta ter muitas sobrecargas. Se você oferecer suporte a qualquer tipo que implemente ISerializable, escreva um método não genérico que aceite ISerializable. Use genéricos para coisas que são realmente genéricas ; se o tipo realmente importa, provavelmente não é genérico.
precisa
@ Eric: Obrigado, eu também estou querendo saber se você pode usar o mesmo critério com números? Por exemplo, para escrever funções matemáticas que suportam todos os tipos numéricos, como Média, Soma, etc. Elas devem ser implementadas usando Generic ou sobrecargas? Importa se a implementação é a mesma ou não? Porque é praticamente a mesma operação para Média, Soma para qualquer tipo numérico, certo?
precisa
1
@Joan: Ser capaz de escrever métodos aritméticos genéricos em tipos restritos à implementação de vários operadores é um recurso frequentemente solicitado, mas requer suporte a CLR e é surpreendentemente complicado. Estamos considerando isso para versões futuras do idioma, mas não promessas.
precisa

Respostas:

182

Você pode usar a propriedade Type.IsPrimitive, mas tenha cuidado, porque existem alguns tipos que podemos pensar que são primitivos, mas eles não são, por exemplo, Decimale String.

Editar 1: código de amostra adicionado

Aqui está um código de exemplo:

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

Edit 2: Como o @SLaks comenta, existem outros tipos que talvez você queira tratar como primitivos também. Eu acho que você terá que adicionar essas variações uma a uma .

Edit 3: IsPrimitive = (Booleano, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double e Single), tipo Anther Primitive-Like para verificar (t == typeof (DateTime )))

Javier
fonte
12
E, talvez DateTime, TimeSpane DateTimeOffset.
SLaks
Mmmm ... sim, você está certo. Eu acho que teremos que adicionar mais algumas possibilidades
Javier
2
Você precisa usar o lógico ou ( ||), não o bit a bit ou ( |).
SLaks
42
Aqui está um método de extensão que escrevi para executar convenientemente os testes descritos nas respostas de @Javier e Michael Petito: gist.github.com/3330614 .
12133 Jonathan
5
Você pode usar a propriedade Type.IsValueType e adicionar apenas a verificação da string.
Matteo Migliore
57

Acabei de encontrar esta pergunta ao procurar uma solução semelhante e pensei que você poderia estar interessado na seguinte abordagem usando System.TypeCodee System.Convert.

É fácil serializar qualquer tipo mapeado para System.TypeCodeoutro que não seja System.TypeCode.Object, para que você possa fazer:

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

A vantagem dessa abordagem é que você não precisa nomear todos os outros tipos não primitivos aceitáveis. Você também pode modificar um pouco o código acima para manipular qualquer tipo que implemente IConvertible.

Michael Petito
fonte
2
Isso é ótimo, eu tive que adicionar manualmente Guidpara meus próprios propósitos (como um primitivo na minha definição).
Erik Philips
56

Fazemos assim em nosso ORM:

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

Eu sei que usar IsValueTypenão é a melhor opção (você pode ter suas próprias estruturas muito complexas), mas funciona em 99% dos casos (e inclui Nullables).

Alex
fonte
6
Por que você precisa de IsPrimitive se você estiver usando IsValueType? Nem todos os tipos de valores primitivos são?
JoelFan
5
O tipo decimal @JoelFan possui IsPrimitive false, mas IsValueType true
xhafan
3
@xhafan: Você responde à pergunta errada. Todas as estruturas são como decimalnesse sentido. Mas existe um tipo para o qual IsPrimitiveretorna truemas IsValueTyperetorna false? Se não houver esse tipo, o t.IsPrimitiveteste será desnecessário.
Lii
6
@Lii você está certo, todo tipo primitivo foi IsValueTypeconfigurado como true, portanto, IsPrimitivenão é necessário procurar . Felicidades!
Xhafan
1
@Veverke Eles não. Você pode ter um tipo de valor não primitivo; nesse caso, as propriedades têm valores diferentes.
Michael Petito
38

A partir da resposta de @Ronnie Overby e do comentário de @jonathanconway, escrevi este método que funciona com o Nullable e exclui as estruturas do usuário.

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

Com o seguinte 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
fonte
1
Essa é uma boa abordagem, mas Enumnão é suportada, teste-a com enum MyEnum { EnumValue }e usando MyEnum. @ Jonathan também está usando type.IsValueType. Com isso, os Enumssão detectados corretamente, mas também Structs. Portanto, cuidado com os primitivos que você deseja.
Apfelkuacha
1
@ Apfelkuacha: você está totalmente certo. Mas ao invés de usar type.IsValueType, por que simplesmente não adicionar type.IsEnum?
Xav987 27/01
Você está totalmente certo. type.IsEnumtambém é possível. Eu sugeri uma edição no seu post :)
Apfelkuacha 28/01
16

Aqui está como eu fiz isso.

   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
fonte
@RonnieOverby. existe alguma razão paticular usada IsAssignableFromem seu teste em vez de conter?
51919 Johnny
6

Também uma boa possibilidade:

private static bool IsPrimitiveType(Type type)
{
    return (type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object);
}
k3flo
fonte
Toda instância de Typepossui uma propriedade chamada IsPrimitive . Você deveria usar isso.
Renan
3
Nem Stringnem Decimalsão primitivos.
K3flo
Isso funciona para mim, mas eu renomeado para IsClrType para não confundir seu significado com o .IsPrimitive existente na classe Tipo
KnarfaLingus
1
Isso não escolhe Guid ou TimeSpan, por exemplo.
Stanislav
3

Supondo que você tenha uma assinatura de função como esta:

void foo<T>() 

Você pode adicionar uma restrição genérica para permitir apenas tipos de valor:

void foo<T>() where T : struct

Observe que isso permite não apenas tipos primitivos para T, mas qualquer tipo de valor.

eWolf
fonte
2

Eu precisava serializar tipos para exportá-los para XML. Para fazer isso, eu iterava pelo objeto e optei por campos que eram primitivos, enum, tipos de valor ou serializáveis. Este foi o resultado da minha 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});

Usei o LINQ para percorrer os tipos e obter seu nome e valor para armazenar em uma tabela de símbolos. A chave está na cláusula 'where' que escolhi para reflexão. Eu escolhi tipos primitivos, enumerados, de valor e tipos serializáveis. Isso permitiu que strings e objetos DateTime passassem como eu esperava.

Felicidades!

JFalcon
fonte
1

É isso que tenho na minha biblioteca. Comentários são bem-vindos.

Verifico IsValueType primeiro, pois ele lida com a maioria dos tipos, depois String, uma vez que é o segundo mais comum. Não consigo pensar em um primitivo que não seja um tipo de valor, então não sei se essa parte do objeto foi atingida.

  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

Então eu posso usá-lo assim:

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

Eu só quero compartilhar minha solução. Talvez seja útil para qualquer pessoa.

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

Não se esqueça de verificar o espaço para nome NULL, porque objetos anônimos não possuem espaço para nome atribuído

iDusko
fonte
0

Aqui está outra opção viável.

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