79
typeof(string).IsPrimitive == false
typeof(int).IsPrimitive == true
typeof(MyClass).IsClass == true
typeof(string).IsClass == true
typeof(string).IsByRef == false
typeof(MyClass).IsByRef == true // correction: should be false (see comments below)

I have a method that instantiates a new instance of T and, if it's a "complex" class, fills its properties from a set of source data values.

(a) If T is a simple type (e.g. a string or an int or anything else similar), a quick conversion from the source data to T is to be performed.

(b) If T is a class (but not something simple like string), then I'll use Activator.CreateInstance and do a bit of reflection to populate the fields.

Is there a quick and simple way to tell if I should use method (a) or method (b)? This logic will be used inside a generic method with T as the type argument.

Nathan Ridley
  • 33,766
  • 35
  • 123
  • 197
  • string, despite having a lowercase name in C#, is not a simple type, but rather a wrapper around a char array. string is converted to String internally by the C# compiler. – Powerlord May 14 '09 at 15:41
  • 2
    I'm curious -- how did you get that last line to result in true? – Eric Lippert May 14 '09 at 16:52
  • because class instances are passed by reference... – Nathan Ridley May 14 '09 at 22:33
  • @EricLippert at the time I wrote the previous comment, I had no idea who you were. Having forgotten all about this question, I just came across it again and noticed my flippant response to your question and realised who I was dealing with, which means obviously I'm wrong ;) – Nathan Ridley Feb 27 '14 at 06:23
  • 3
    @NathanRidley: `IsByRef` is for the following situation: you have a `MethodInfo` for method `void A(ref int x)`; you obtain the `ParameterInfo` for `x` and ask it for the parameter's type. The `IsByRef` property of that `Type` should be `true`. The `GetElementType` method then returns `typeof(int)`. I cannot think of a way to use `typeof(C)` and get a *ref parameter type* out of that, hence my question. – Eric Lippert Feb 27 '14 at 14:50
  • 2
    Your supposition that `IsByRef` tells you whether a value of a type is passed by reference is clearly incorrect because you stated that `IsByRef` was `false` for `string`, but clearly strings are passed by reference. To tell whether a type is passed by reference or by value you should use `IsValueType`. – Eric Lippert Feb 27 '14 at 14:53
  • @EricLippert - thanks, that makes perfect sense – Nathan Ridley Feb 28 '14 at 06:36

7 Answers7

144

String is probably a special case.

I think I would do.....

bool IsSimple(Type type)
{
    return type.IsPrimitive 
      || type.Equals(typeof(string));
}

Edit:

Sometimes you need to cover some more cases, like enums and decimals. Enums are a special kind of type in C#. Decimals are structs like any other. The problem with the structs is that they may be complex, they may be user defined types, they may be just a number. So you don't have any other chance than knowing them to differentiate.

bool IsSimple(Type type)
{
  return type.IsPrimitive 
    || type.IsEnum
    || type.Equals(typeof(string))
    || type.Equals(typeof(decimal));
}

Handling nullable counterparts are also a bit tricky. The nullable itself is a struct.

bool IsSimple(Type type)
{
  if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
  {
    // nullable type, check if the nested type is simple.
    return IsSimple(type.GetGenericArguments()[0]);
  }
  return type.IsPrimitive 
    || type.IsEnum
    || type.Equals(typeof(string))
    || type.Equals(typeof(decimal));
}

Test:

Assert.IsTrue(IsSimple(typeof(string)));
Assert.IsTrue(IsSimple(typeof(int)));
Assert.IsTrue(IsSimple(typeof(decimal)));
Assert.IsTrue(IsSimple(typeof(float)));
Assert.IsTrue(IsSimple(typeof(StringComparison)));  // enum
Assert.IsTrue(IsSimple(typeof(int?)));
Assert.IsTrue(IsSimple(typeof(decimal?)));
Assert.IsTrue(IsSimple(typeof(StringComparison?)));
Assert.IsFalse(IsSimple(typeof(object)));
Assert.IsFalse(IsSimple(typeof(Point)));  // struct in System.Drawing
Assert.IsFalse(IsSimple(typeof(Point?)));
Assert.IsFalse(IsSimple(typeof(StringBuilder))); // reference type

Note to .NET Core

As DucoJ points out in his answer, some of the used methods are not available on the class Type in .NET core anymore.

Fixed code (I hope it works, I couldn't try myself. Otherwise please comment):

bool IsSimple(Type type)
{
  var typeInfo = type.GetTypeInfo();
  if (typeInfo.IsGenericType && typeInfo.GetGenericTypeDefinition() == typeof(Nullable<>))
  {
    // nullable type, check if the nested type is simple.
    return IsSimple(typeInfo.GetGenericArguments()[0]);
  }
  return typeInfo.IsPrimitive 
    || typeInfo.IsEnum
    || type.Equals(typeof(string))
    || type.Equals(typeof(decimal));
}
DerApe
  • 3,097
  • 2
  • 35
  • 55
Stefan Steinegger
  • 63,782
  • 15
  • 129
  • 193
  • John Saunders, below, makes a valid point, so in that light I think your answer is probably the best one for my case. – Nathan Ridley May 14 '09 at 15:32
  • This fails for things like enums, decimals, etc. See my answer below for a more general solution – Mike Vonn Oct 08 '15 at 15:19
  • double is also "primitive" according to https://learn.microsoft.com/en-us/dotnet/api/system.type.isprimitive?view=netframework-4.7.1 – AndyPook Mar 21 '18 at 15:41
  • @AndyPook: Yes, it is. What do you mean by that? I didn't list all the primitive types in the "tests", just a selection of a few different cases. `double` is the same as `float`, `int`, `bool`, `long`, 'byte` and so on. Special cases are `decimal`, `string` and enums which are not primitive but still usually regarded as "simple" types. – Stefan Steinegger Mar 26 '18 at 10:27
  • According to that link `IsPrimative` is true for `double`. So it's not "special" and doesn't need the extra test in `IsSimple`. right? – AndyPook Mar 26 '18 at 10:34
  • 5
    Depending on your needs, you might need to add **|| type.Equals(typeof(datetime))** – smirkingman May 31 '19 at 08:22
  • 1
    As well as `|| type.Equals(typeof(TimeSpan))`. – NetMage Jun 10 '19 at 23:50
  • Also, note that .Net Core supports the .Net `Type` again to ease porting. – NetMage Jun 10 '19 at 23:53
  • 1
    DateTime also needs to be checked for – Francine DeGrood Taylor Sep 11 '20 at 01:28
  • If you have a look at the https://learn.microsoft.com/en-us/dotnet/api/system.valuetype?view=netcore-3.1 the Enum and Decimal is a part of ValueType so you could simplify what the `IsSimple` returns to just `return typeInfo.IsPrimitive || type.Equals(typeof(string)) || type.IsSubclassOf(typeof(ValueType));` – GoldenAge Mar 17 '21 at 12:46
  • @GoldenAge: a Struct is a value type too, but not necessarily a simple type (it may have multiple fields). The definition of a "simple type" is not a technical one and most probably specific to a project. – Stefan Steinegger Mar 23 '21 at 07:56
17

In Addition to Stefan Steinegger answer: In .NET Core the .IsPrimitive etc. are no longer members of Type, they are now members of TypeInfo. So his solution will then become:

bool IsSimple(TypeInfo type)
{
    if (type.IsGenericType && type.GetGenericTypeDefinition() ==     typeof(Nullable<>))
    {
        // nullable type, check if the nested type is simple.
        return IsSimple((type.GetGenericArguments()[0]).GetTypeInfo());
    }
    return type.IsPrimitive
      || type.IsEnum
      || type.Equals(typeof(string))
      || type.Equals(typeof(decimal));
}
iCantSeeSharp
  • 3,880
  • 4
  • 42
  • 65
DucoJ
  • 171
  • 1
  • 2
  • If somebody with enough rep could post this as a comment under Stefan Steinegger's post, it might safe other people time. – DucoJ Aug 17 '16 at 13:44
  • At least in the version of netstandard I am using (1.6) `type.Equals(typeof(x))` results in a compile error because the `type` variable is a `System.Reflection.TypeInfo`, which does *not* derive from `System.Type`. Instead you have to use `type.Equals(typeof(x).GetTypeInfo())`. – Dave Parker Sep 11 '17 at 18:44
  • @DucoJ: Thanks for this. I didn't know that. I added this information to my answer. – Stefan Steinegger Mar 26 '18 at 10:40
16

Using a solution based on TypeConverter is also a nice and simple way to model this.

Say you have this implementation for instance:

public static bool IsSimple(this Type type) =>
    TypeDescriptor.GetConverter(type).CanConvertFrom(typeof(string));

This returns true for:

  • All primitive types
  • All enums
  • strings
  • decimals
  • DateTime
  • DateTimeOffset
  • TimeSpan
  • Uri
  • Guid
  • Nullable<> of any of the types above
  • numerous other types that have native TypeConverters implemented (see here on the Derived section)

This approach works well since most frameworks support TypeConverters natively, like XML and Json serialization libraries, and you can then use the same converter to parse the values while reading.

julealgon
  • 7,072
  • 3
  • 32
  • 77
7

There is a more general type than primitive, the ValueType encompasses a lot more than primitive, such as enums, decimal, and other such things ValueType. Below is a function I wrote to identify complex types, that may fit your needs.

    public static bool IsComplex(Type typeIn)
    {
        if (typeIn.IsSubclassOf(typeof(System.ValueType)) || typeIn.Equals(typeof(string))) //|| typeIn.IsPrimitive
            return false;
        else
            return true;

    }
Mike Vonn
  • 182
  • 3
  • 8
  • 3
    There is also `typeIn.IsValueType`. "IsComplex" is misleading in this case, because a struct (value type) can be complex by having many properties, which for instance can point to reference types. – Stefan Steinegger Oct 09 '15 at 04:50
  • I improved my answer. There is no single correct answer to this, because it depends very much on what is needed in the specific case. Sometimes you can handle all the value types the same, sometimes you can not. – Stefan Steinegger Oct 09 '15 at 05:39
2

Sorry to resurrect a really old thread, but since this still ranks high on web searches in Google, want to get a more direct and effective solution added:

if(System.Type.GetTypeCode(typeof(int)) == TypeCode.Object) {
    // Do what you will...
}
Capt Nasty
  • 89
  • 2
  • 9
    You should explain a little better what's going on here, i.e. why performing that particular check is useful. – Nathan Ridley Sep 03 '15 at 02:19
  • 1
    When `System.Type.GetTypeCode(typeof(myValue))` is `TypeCode.Object`, "myValue" has a non primitive type. Else there are a dozen of different primitive types. https://msdn.microsoft.com/en-us/library/system.typecode(v=vs.110).aspx – barbara.post Jan 06 '17 at 21:40
  • 1
    This doesn't work so well for Nullables.... `>System.Type.GetTypeCode(typeof(int?))` `>Object` – zaitsman Oct 03 '17 at 23:17
0

Modified Mauser's answer a little bit added a method to check whether a property is an collection.

public static class TypeExtensions
{
    public static bool IsComplex(this Type type)
    {
        return !type.IsValueType && type != typeof(string);
    }

    public static bool IsCollection(this Type type)
    {
        var collectionTypeName = typeof(ICollection<>).Name;
        return type.Name == collectionTypeName || type.GetInterface(collectionTypeName) != null;
    }
}

Here inside IsCollection(..) we can even keep IEnumerable, but string also inherit IEnumerable. so if you are using IEnumerable, add a check for string also!

public static class TypeExtensions
    {

        public static bool IsComplex(this Type type)
        {
            return !type.IsValueType && type != typeof(string);
        }



        public static bool IsCustomComplex(this Type type)
        {
            var elementType = type.GetCustomElementType();
            return elementType != null && elementType.IsComplex();
        }

        public static Type GetCustomElementType(this Type type, object value)
        {
            return value != null 
                ? value.GetType().GetCustomElementType() 
                : type.GetCustomElementType();
        }

        public static Type GetCustomElementType(this Type type)
        {
            return type.IsCollection()
                ? type.IsArray
                    ? type.GetElementType()
                    : type.GetGenericArguments()[0]
                : type;
        }


        public static bool IsCustomComplex(this Type type, object value)
        {
            return value != null
                ? value.GetType().IsCustomComplex()
                : type.IsCustomComplex();
        }


        public static bool IsCollection(this Type type)
        {
            var collectionTypeName = typeof(IEnumerable<>).Name;
            return (type.Name == collectionTypeName || type.GetInterface(typeof(IEnumerable<>).Name) != null ||
                    type.IsArray) && type != typeof(string);
        }

        public static bool HasDefaultConstructor(this Type type)
        {
            return type.IsValueType || type.GetConstructor(Type.EmptyTypes) != null;
        }

    }
Shyam Kumar
  • 96
  • 1
  • 3
-1

Strings aren't primitives, if I recall correctly. even though there is a keyword for it, a string is an object. Your call to IsPrimitive will accurately tell you if something is a primitive.

Annath
  • 1,262
  • 10
  • 20
  • Yes but strings are assigned like primitives and in most cases are treated (from the outside) as though they are primitives. – Nathan Ridley May 14 '09 at 15:23
  • 1
    Strings are kind of a unique beast in this regard. I think Stefan's suggestion of special handling for them is the best approach. – Reed Copsey May 14 '09 at 15:25
  • This does not answer the question at hand and would be better suited as a comment. – julealgon Nov 30 '20 at 19:29