1

I have a method with an IEnumerable<T> parameter.
T can be one of the built-in .NET types like int or string or a custom class like this:

class MyClass
{
    public string Foo{ get; set; }
    public int Bar{ get; set; }
}

How can I recognize programmatically if T is one of the built-in types?

I know that I can do something like this:

switch (typeof(T).Name.ToLower())
{
    case "int":          
    case "string":
    case "...":   // and so on...
        Console.WriteLine("It's a built-in type!");
        break;
    default:
        Console.WriteLine("It's a custom class!");
        break;
}

...but there must be a shorter/simpler way, right?


EDIT:

Okay guys, thank you very much for the answers so far.
But I'm still not sure which one is the best for my situation.

What I actually want to do is this:
I'm writing a library to convert IEnumerable<T>s into ADODB.Recordsets.
At the beginning of each conversion, I need to create an empty Recordset and add fields to it.

If T is a custom class, I have to loop through its properties and create a field in the Recordset for each property of T (with the property's name and type).

But looping through the properties only works properly if T is a custom class.
For example, it T is a string, I get the properties of the string (Chars and Length), which are of no use for me in this case.

This means that only checking if it's a primitive type is not enough - I need to recognize things like DateTime and GUID as well, and there are probably even more.
(I have to admit, I didn't notice that DateTime is not in the list of built-in types).

So I guess what I actually want is:
Tell if T has user-defined properties which I can loop, or not.
(no matter if it has no properties at all like int , or properties which I don't care about like string has)
Does this make sense?

However, I'm still not sure which answer to pick.
driis' and Jon Skeet's answer both mean that I basically have to list a lot of types (more in Jon's answer than driis' answer).
At the moment, I tend to pick Ron Sijm's answer (even though people apparently liked the other answers more), because I guess simply checking "System." is the shortest way to do what I want, even if it does not look, well, that elegant...

Community
  • 1
  • 1
Christian Specht
  • 35,843
  • 15
  • 128
  • 182
  • 2
    possible duplicate of [Is there a function to check if an object is a builtin data type?](http://stackoverflow.com/questions/1114799/is-there-a-function-to-check-if-an-object-is-a-builtin-data-type) – Damith Nov 06 '11 at 17:54
  • In case anyone is interested, I just wrote an [answer](http://stackoverflow.com/questions/8029023/how-to-tell-an-ienumerableint-from-an-ienumerablemyclass/8056852#8056852) about why I picked Jon's answer and how my actual solution looks like. – Christian Specht Nov 08 '11 at 20:46

5 Answers5

6

It depends on what you define as "built-in types". In many cases, you could look at whether the type is primitive, or string. (Because string is considered "built-in", but it is not a primitive).

if (typeof(T).IsPrimitive || typeof(T) == typeof(string))
    Console.WriteLine("It's a built-in type");

This works, if you are satisfied with these primitives (from MSDN):

The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.

Remember that the typename intis just a C# alias for Int32.

Edit

For determining types that can be used as a field in a recordset directly, I would probably look at IsPrimitive first, and then have a HashSet of other "single-valued types" that are directly supported by ADO. A brainstorm of types to include turns up Guid, Decimal, string and DateTime. I don't think there are too many others, but I might be wrong.

Sure, types that reside directly in the System namespace would be a simple approach, but you will be in trouble the first time someone passes you a System.AppDomain or a System.Uri. Basically, if you look at what's in the System namespace, the vast majority of types is not something you should try to put in a single field.

driis
  • 161,458
  • 45
  • 265
  • 341
5

What you determine to be a "built-in type" is likely to be context-specific - you've listed the types built into the C# language, for example, but that really is specific to C#. It includes decimal (which isn't a CLR primitive type) for example, but not DateTime (which other languages could support explicitly).

So, I'd just use a HashSet<Type> which you create appropriately:

private static readonly HashSet<Type> BuiltInTypes = new HashSet<Type>
{
    typeof(object), typeof(string), typeof(byte), typeof(sbyte),
    // etc
};

// Then:
if (BuiltInTypes.Contains(typeof(T)))
{
    Console.WriteLine("It's a built-in type!");
}
else
{
    Console.WriteLine("It's a custom class!");
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Good point, I wasn't aware of the fact that the built-in types are not the same in all CLR languages. I just edited the question to explain what I actually want to do (convert `IEnumerable` to `ADODB.Recordset`). So I actually need **two** different type systems (.net and ADO)...and I guess it will be hard to get that to work without explicitly listing the types, as you suggested. – Christian Specht Nov 06 '11 at 19:13
0

You could use (typeof(T).FullName.StartsWith("System."))

Ron Sijm
  • 8,490
  • 2
  • 31
  • 48
  • But System.DateTime, and others, is not a "built-in type". – driis Nov 06 '11 at 17:57
  • 1
    yea, you're right, its not one of the "built-in types". but because of his code sample, it seemed to me like he just wanted to check it if was something he made himself (like his MyClass), or if it was something that was "build in" – Ron Sijm Nov 06 '11 at 18:07
  • DateTime is provided by Microsoft as part of .NET. Sounds built in to me. Though I'd check `typeof(T).Assembly`, since I don't think .NET prohibits you using the System namespace for your own classes. – David Yaw Nov 06 '11 at 18:22
  • Actually, this is exactly what I wanted to check: self-made class vs. something built in. I just edited my question to provide more details. – Christian Specht Nov 06 '11 at 19:07
0

I like to use the Type.GetTypeCode() for dealing with database 'primitives'. You can have a simple condition that tells you if it's a database primitive as so:

bool isDBPrimitive = Type.GetTypeCode(typeof(T)) != Object;

The return type of GetTypeCode() is the TypeCode enumeration which will return one of the below enumeration values if the type matches, otherwise it will return the TypeCode.Object enumeration value:

DBNull
Boolean
Char
SByte
Byte
Int16
UInt16
Int32
UInt32
Int64
UInt64
Single
Double
Decimal
DateTime
String

Notable omissions are Guid, DateTimeOffset, and nullable types (although you can use typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>) to check if an object is nullable and then use Nullable.GetUnderlyingType(typeof(T)) method to get a non-nullable type which will work with GetTypeCode().

Allon Guralnek
  • 15,813
  • 6
  • 60
  • 93
0

I had a hard time deciding if I like driis' or Jon's answer better, but at the end I chose to accept Jon's answer.

For anyone interested, I wanted to elaborate a bit on why I considered this answer the best solution for me:

At first glance I liked .IsPrimitive better than populating a HashSet with all the types.
But once the special cases like stringand datetime were mentioned, it was clear that I wouldn't get away with a short solution like if (typeof(T).IsPrimitive) anyway.

So I thought more about my actual use case (converting from IEnumerable<T> to ADODB.Recordset) and I noticed that for the actual conversion of CLR types to ADO types, I would need lists of both kind of types anyway.
(to know that an Int16 needs to be converted into an ADODB.DataTypeEnum.adSmallInt, and so on)

So at the end I did something similar like Jon suggested, but I used a Dictionary to hold my supported CLR types and the corresponding ADO types:

internal static class DataTypes
{
    private static readonly Dictionary<Type, ADODB.DataTypeEnum> Types
        = new Dictionary<Type, ADODB.DataTypeEnum>()
        {
            { typeof(Boolean), ADODB.DataTypeEnum.adBoolean },
            { typeof(DateTime), ADODB.DataTypeEnum.adDate },
            // and so on...
        };

    public static bool TryGetAdoTypeForClrType(Type clrType, out ADODB.DataTypeEnum adoType)
    {
        return Types.TryGetValue(clrType, out adoType);
    }
}

I can use this later for the actual work of converting my POCO properties to ADO fields.

And I can also check the T of an IEnumerable<T> before I start converting, because if TryGetAdoTypeForClrType(typeof(T), ...) finds something, then T can't be a POCO / custom class.

This might not be the most elegant solution if you need to tell an IEnumerable<int> from an IEnumerable<MyClass> for other reasons than I do, but I think it's at least the best solution for my use case now.

Christian Specht
  • 35,843
  • 15
  • 128
  • 182