Reputation: 42175
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.
Upvotes: 203
Views: 121324
Reputation: 46470
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;
}
}
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);
}
}
Upvotes: 36
Reputation: 3771
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);
}
Upvotes: 0
Reputation: 1242
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?)));
}
Upvotes: 58
Reputation: 60606
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).
Upvotes: 63
Reputation: 375
Also a good possibility:
private static bool IsPrimitiveType(Type type)
{
return (type == typeof(object) || Type.GetTypeCode(type) != TypeCode.Object);
}
Upvotes: 7
Reputation: 453
Here is another viable option.
public static bool CanDirectlyCompare(Type type)
{
return typeof(IComparable).IsAssignableFrom(type) || type.IsPrimitive || type.IsValueType;
}
Upvotes: 0
Reputation: 4141
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))
Upvotes: 213
Reputation: 53
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
Upvotes: 1
Reputation: 195
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");
}
Upvotes: 0
Reputation: 179
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!
Upvotes: 2
Reputation: 22406
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
Upvotes: 1
Reputation: 13161
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.
Upvotes: 68
Reputation: 3629
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.
Upvotes: 3