How To Test if Type is Primitive
Asked Answered
A

13

200

I have a block of code that serializes a type into a Html tag.

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

This works great, except I want it to only do this for primitive types, like int, double, bool etc, and other types that aren't primitive but can be serialized easily like string. I want it to ignore everything else like Lists & other custom types.

Can anyone suggest how I do this? Or do I need to specify the types I want to allow somewhere and switch on the property's type to see if it's allowed? That's a little messy, so it'd be nice if I there was a tidier way.

Asphalt answered 14/3, 2010 at 14:58 Comment(4)
System.String is not a primitive type.Dissert
The better way to do it is to not use generics at all. If you support a small number of types as legal parameter types then simply have that many overloads. If you support any type that implements ISerializable, then write a non-generic method that takes an ISerializable. Use generics for things which are actually generic; if the type actually matters, its probably not generic.Manicurist
@Eric: Thanks, I am also wondering if you can use the same criteria with numerics? For instance to write mathematical functions that support all numeric types, i.e. Average, Sum, etc. Should they be implemented using Generic or overloads? Does it matter whether the implementation is the same or not? Because it's pretty much the same operation for Average, Sum for any numeric type, right?Arevalo
@Joan: Being able to write generic arithmetic methods on types constrained to implement various operators is a frequently requested feature, but it requires CLR support and is surprisingly complicated. We're considering it for future versions of the language, but no promises.Manicurist
R
211

You can use the property Type.IsPrimitive, but be carefull because there are some types that we can think that are primitives, but they aren´t, for example Decimal and String.

Edit 1: Added sample code

Here is a sample code:

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

Edit 2: As @SLaks comments, there are other types that maybe you want to treat as primitives, too. I think that you´ll have to add this variations one by one.

Edit 3: IsPrimitive = (Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single), Anther Primitive-Like type to check (t == typeof(DateTime))

Rolling answered 14/3, 2010 at 15:2 Comment(11)
And perhaps DateTime, TimeSpan, and DateTimeOffset.Dissert
Mmmm... yes, you are right. I think we´ll have to add some more possibilitiesRolling
You need to use the logical or (||), not the bitwise or (|).Dissert
I should test the code in and IDE before writing here, hehe =PRolling
Mmmm... I´ve heard about LINQPad, for test LINQ queries, I works to test code too? definitely I´ll try it. thanksRolling
@SLaks, I didn't notice the | - can you explain why I have to use logical instead of bitwise? - thanksAsphalt
You did´n notice because I edited my post. I had a mistake, I used | instead or ||. Now is right.Rolling
Here's an extension method I wrote to conveniently run the tests described in the answers by @Rolling and Michael Petito: gist.github.com/3330614.Addressograph
You can use the property Type.IsValueType and add only the check for the string.Vomer
And Guid, DBNullBuchheim
Maybe and Version.Buchheim
C
66

I just found this question while looking for a similar solution, and thought you might be interested in the following approach using System.TypeCode and System.Convert.

It is easy to serialize any type that is mapped to a System.TypeCode other than System.TypeCode.Object, so you could do:

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

The advantage with this approach is you don't have to name every other acceptable non-primitive type. You could also modify the above code slightly to handle any type that implements IConvertible.

Cinchonize answered 18/4, 2010 at 23:31 Comment(1)
This is great, I had to manually add Guid for my own purposes (as a primitive in my definition).Wallsend
S
63

We do it like this in our ORM:

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

I know that using IsValueType is not the best option (you can have your own very complex structs) but it works in 99% cases (and includes Nullables).

Seafowl answered 14/5, 2012 at 10:45 Comment(9)
Why do you need IsPrimitive if you are using IsValueType? Are not all primitives value types?Kellikellia
@Kellikellia decimal type has IsPrimitive false, but IsValueType trueRenounce
@xhafan: You answer the wrong question. All structs are like decimal in that regard. But are there a type for which IsPrimitive returns true but IsValueType returns false? If there are no such type then the t.IsPrimitive test is unnecessary.Poplin
@Poplin you are right, every primitive type has IsValueType set to true, so checking for IsPrimitive is not need. Cheers!Renounce
So what is then the answer to "why does the framework provide 2 properties that represent the very same data" ?Grained
@Grained They don't. You can have a non-primitive value type, in which case the properties have different values.Cinchonize
@MichaelPetito: am I correct in saying that the only scenario such thing could happen is when you have a user-defined struct ?Grained
@Grained No. For instance, System.Decimal is a non-primitive value type. So is System.Guid.Cinchonize
@MichaelPetito: but both are structs. I should have stated "... only scenario this could happen is with a struct".Grained
C
58

From @Ronnie Overby response and @jonathanconway comment, I wrote this method that works for Nullable, and exclude user structs.

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

With the following 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?)));
}
Craunch answered 1/9, 2015 at 17:47 Comment(4)
This is a good approach, but Enum is not supported, test it with enum MyEnum { EnumValue } and using MyEnum. @Addressograph is also using type.IsValueType. With that the Enums are correctly detected, but also Structs. So watch out what primitives you want.Paroicous
@Apfelkuacha: you are totally right. But instead using type.IsValueType, why simply not add type.IsEnum?Craunch
you are totally right. type.IsEnum is also possible. I've suggested an edit on your post :)Paroicous
Convert.GetTypeCode(type) always returns Object. Also, I'd get rid of that Array allocation for the contains check and convert those type checks to boolean expressions.Katinakatine
K
35

Here's how I did it.

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

2023 update

Derived from @Xav987's answer, this performs better and has less code.

    static readonly ConcurrentDictionary<Type, bool> IsSimpleTypeCache = new ConcurrentDictionary<System.Type, bool>();

    public static bool IsSimpleType(Type type)
    {
        return IsSimpleTypeCache.GetOrAdd(type, t =>
            type.IsPrimitive ||
            type.IsEnum ||
            type == typeof(string) ||
            type == typeof(decimal) ||
            type == typeof(DateTime) ||
            type == typeof(DateOnly) ||
            type == typeof(TimeOnly) ||
            type == typeof(DateTimeOffset) ||
            type == typeof(TimeSpan) ||
            type == typeof(Guid) ||
            IsNullableSimpleType(type));

        static bool IsNullableSimpleType(Type t)
        {
            var underlyingType = Nullable.GetUnderlyingType(t);
            return underlyingType != null && IsSimpleType(underlyingType);
        }
    }

Katinakatine answered 22/3, 2013 at 19:8 Comment(1)
@RonnieOverby. is there any paticular reason you use IsAssignableFrom in your test instead of contains?Alaynaalayne
P
7

Also a good possibility:

private static bool IsPrimitiveType(Type type)
{
    return (type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object);
}
Procrustean answered 14/6, 2013 at 13:16 Comment(4)
Every instance of Type has a property called IsPrimitive. You should use that instead.Vudimir
Neither String nor Decimal are primitives.Procrustean
This works for me, but I renamed to IsClrType to not confuse its meaning with the existing .IsPrimitive on the Type classBitten
This won't pick Guid or TimeSpan, for example.Lanna
S
3

Assuming you have a function signature like this:

void foo<T>() 

You could add a generic constraint to allow value types only:

void foo<T>() where T : struct

Notice that this allows not only primitive types for T, but any value type.

Stomach answered 14/3, 2010 at 15:4 Comment(0)
T
2

I had a need to serialize types for the purposes of exporting them to XML. To do this, I iterated through the object and opted for fields that were primitive, enum, value types or serializable. This was the result of my query:

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

I used LINQ to iterate through the types, then get their name and value to store in a symbol table. The key is in the 'where' clause that I chose for reflection. I chose primitive, enumerated, value types and serializable types. This allowed for strings and DateTime objects to come through as I expected.

Cheers!

Tuberous answered 18/5, 2013 at 14:55 Comment(0)
I
1

This is what I have in my library. Comments are welcome.

I check IsValueType first, since it handles most types, then String, since it's the second most common. I can't think of a primitive that isn't a value type, so I don't know if that leg of the if ever gets hit.

  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

Then I can use it like this:

  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
Infare answered 22/5, 2012 at 16:38 Comment(0)
H
1
public static bool IsPrimitiveType(object myObject)
{
   var myType = myObject.GetType();
   return myType.IsPrimitive || myType.Namespace == null ||  myType.Namespace.Equals("System");
}

Don't forget to check NULL namespace, because anonymous objects don't have assigned namespace

Hamachi answered 14/5, 2015 at 21:20 Comment(0)
M
0

I just want to share my solution. Perhaps it's useful to anyone.

public static bool IsPrimitiveType(Type fieldType)
{
   return fieldType.IsPrimitive || fieldType.Namespace.Equals("System");
}
Memento answered 27/8, 2013 at 8:7 Comment(2)
IsPrimitiveType(typeof(System.AccessViolationException)) == trueKatinakatine
namespace System { class MyNonPrimitiveType { } }Katinakatine
L
0

Here is another viable option.

public static bool CanDirectlyCompare(Type type)
{
    return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
Lynlyncean answered 9/12, 2015 at 22:39 Comment(0)
I
0

An Extension method which works only for Primitive types, String, Decimal, DateTime, DateTimeOffset, TimeSpan and Guid.
You can add type.IsValueType in OR if you want to include generics, structs and enums.

    private static readonly Type[] PrimitiveLikeTypes = new[]
    {
                typeof(string),
                typeof(decimal),
                typeof(DateTime),
                typeof(DateTimeOffset),
                typeof(TimeSpan),
                typeof(Guid)
    };

    /// <summary>
    /// Determine whether a type is simple (Primitive, String, Decimal, DateTime, etc) 
    /// or complex (i.e. structs, Enums, custom class with public properties and methods).
    /// Returns false for structs and Enums
    /// </summary>
    /// <param name="type">System.Type</param>
    /// <returns> boolean value indicating whether the type is simple or not</returns>
    public static bool IsSimpleType(this Type type)
    {
        return type.IsPrimitive || PrimitiveLikeTypes.Contains(type);
    }
    
Ironist answered 31/1, 2022 at 18:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.