How do I tell if a type is a "simple" type? i.e. holds a single value

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.

44268 次浏览

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

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.

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;


}

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

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

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


}

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.