Tuesday, March 9, 2010

C# generic list : How to get the type and object of T?

Today I faced an interesting problem regarding GridView. We are using a enhanced GridView control as a reusable component in different applications.

In one of the application, we got an issue : when all the rows in the Gridview get deleted - we still need to show the Header portion of the Gridview with the "No rows" information displayed on the first row.

Its very simple to do, just bind the GridView to its datasource with 1 empty row and then while rendering, remove all controls from the 1st row and render only the label. The problem we faced was we did not know the Type of GridView's datasource. We can not hardcode as its a bad practice and also we did not know which types are being binded to the Gridview by the Page developers of the application.

So we have to find out the type of the datasource then create an instance of it and then add a new object of that type in to the datasource so the gridview gets rendered. I tried something like :


Type T = GridView1.DataSource.GetType();
T newRow = (T)Activator.CreateInstance(T);


This is something I wanted but the problem is that Activator.CreateInstance() returns an object of Type Object and the generic T can not be used to cast it. This gives a compilation error. Without the cast, I cannot do something like :

GridView1.DataSource.Add(newRow);

which is what I want. To overcome it I had to somehow force .NET to recognize the Object as of Type T and allow me to add it to the GridView1.DataSource.

To achieve it, I tried a hack which worked. Its a mix of reflection and generics and looks like:


Type T = GridView1.DataSource.GetType();
Type V = T.GetGenericArguments()[0];

Object o = Activator.CreateInstance(V);
Object[] ObjectArray = new Object[]{o };
T.GetMethod("Add").Invoke(GridView1.DataSource, ObjectArray);
GridView1.DataBind();


As the datasource for the GridView always implements IEnumerable interface we did the following steps:
  1. First get the Type of the Datasource which was like List
  2. Used "GetGenericArguments" to get the Type of the Object of the GenericType object.
  3. Created an instance of that GenericType
  4. We need an Array of object to call the reflection method.
  5. Use Reflection to add the Add method and pass the Object Array hosting the GenericType object.
  6. Bind the Grid

Now although .NET did not allow us the explicitly cast the GenericObject to its Type, it allowed us to do it in runtime.

I think, .NET should allow us to do in explicitly as thats more intuitive than this cumbersome way. Anyone having a better approach to this problem , please comment below.

No comments: