Tuesday, June 20, 2006

What is System.RuntimeType?

I was trying to get the metadata for a query coming back from SQL Server. I'm not interested in the data, I just wanted to know what the columns were in a particular table. So I was executing a query like so - 'SELECT * FROM blah WHERE 1=0'. When I executed it, I took the DataReader returned and used GetName to get the names of the columns and GetFieldType to find out the types. Perhaps there is a better way to do this but I'm unaware of it. But weirdly, GetFieldType returned System.RuntimeType for every column. I thought WTF is that? A quick Google then a quick Yahoo and quick MSN didn't show up anything useful. The only thing I could find was the comments from the MS Rotor source which said

"RuntimeType is the basic Type object representing classes as found in the system. This type is never creatable by users, only by the system itself. The internal structure is known about by the runtime. __RuntimeXXX classes are created only once per object in the system and support == comparisions."

But this still wasn't much help. So I thought I'd have a hack around. The reason it isn't documented and doesn't show in many searches is because it's an internal class, which makes it a bit surprising I'm seeing at all. But it does have a useful property called UnderlyingSystemType that returns the type I'm interested in. Of course because it's internal I can't access that property. The way round this is via the best thing since sliced bread, reflection. So here is the litle snippet of code I came up with to do just that.
object val = reader.GetFieldType(index);
Type runtimeType = val.GetType();
PropertyInfo propInfo = runtimeType.GetProperty("UnderlyingSystemType");
Type type = (Type)propInfo.GetValue(val, null);

Update - As mentioned in the comments (only took me three years to respond!), this code is actually pointless. The thing I hadn't noticed is that RuntimeType inherits from Type, so although it isn't possible to deal with an instance of RuntimeType directly, you can just treat it as an instance of Type instead.


Gavin said...

Awesome example to get the underlying RuntimeType; saved me a couple of hours.


Anonymous said...

Passing a generic instance to an extension method for object, I needed access to System.RuntimeType in order to check IsGenericType. You provided a very clever solution to getting it. Thanks!

Anonymous said...

You can use the GetSchemaTable() method of the reader to return you information about the columns.

Also a value of RuntimeType can be cast to Type.

Mr. Helper said...

Here's a useful snippet I put together for this very thing:

If sqlReader.HasRows() Then
For Each row As DataRow In sqlReader.GetSchemaTable.Rows
Debug.Write(String.Format("""{0}""", "ColumnName = " & row("ColumnName")))
Debug.Write(String.Format("""{0}""", "DataType = " & row("DataType").Name))
Debug.Write(String.Format("""{0}""", "ProviderSpecificDataType = " & row("ProviderSpecificDataType").Name))
Debug.Write(String.Format("""{0}""", "DataTypeName = " & row("DataTypeName")))
End If

Appel said...

I just have to say, usually never comment on useful information Google produces, but, this has saved my ass. Thanks a bundle!