tags:

views:

250

answers:

4

I'm having a spot of trouble with generics in C#. I have to store a number of generic objects together but their type parameter differs so I have made a non-generic interface which they implement. What I'm looking for is a way to convert back to the generic version, given a type object. I know I can do it with reflection but I was wondering if there was a better/more elegant solution.

The following code illustrates the problem:

interface ITable
{
   public Type Type { get; }
}

class Table<T> : ITable
{
   public Type Type { get{ return typeof(T); } }
}

class Program
{
   static void Main(string[] args)
   {
      var tables = new Dictionary<string, ITable>();
      ... //insert tables
      DoStuffWithTable(tables["my table"]); //This doesn't work
   }

   public static void DoStuffWithTable<T>(Table<T> table)
   {
      ...//Some work
   }
}

Is there a clean way for me to invoke the generic DoStuffWithTable method based on the instance of the Type object I can get from its interface method?

+2  A: 

If you are starting from a non-generic type (ITable), then the only way to do this is via reflection (MakeGenericMethod). It isn't very pretty or especially fast, but it works...

public static void DoStuffWithUntypedTable(ITable table)
{
    typeof(Program).GetMethod("DoStuffWithTable")
        .MakeGenericMethod(table.Type)
        .Invoke(null, new object[] { table });
}

As an aside - note that there is a bit of risk in assuming that an ITable is actually a Table<T> - you should probably verify that, and maybe also use an interface (ITable<T>).


Edit: if it really must be a Table<T>, then you can enforce this (including subclass support, such as FooTable : Table<Foo> as:

public static void DoStuffWithUntypedTable(object table)
{
    Type type = table.GetType();
    while (type != typeof(object))
    {
        if (type.IsGenericType && type.GetGenericTypeDefinition()
              == typeof(Table<>))
        {
            typeof(Program).GetMethod("DoStuffWithTable")
                .MakeGenericMethod(type.GetGenericArguments()[0])
                .Invoke(null, new object[] { table });
            return;
        }
        type = type.BaseType;
    }
    throw new ArgumentException("Not a Table<T> or subclass");
}
Marc Gravell
I think this is a case of using a sledgehammer to bang in a drawing pin. I think this is a design/understanding the language issue. I might be wrong though. I would class the above as a code smell (somewhere between a pungent brie and slightly out of date eggs).
Skizz
Agree with the code smell
Stefan Steinegger
Yes, but given the requirement, and the fact that the classes come from a 3rd party library, it is the only viable route. If you believe otherwise, please show with code ;-p
Marc Gravell
Enforcing could be much simpler:typeof(Table<>).MakeGenericType(table.Type).IsInstanceOfType(table)
configurator
@configurator - good spot; that would probably work fine.
Marc Gravell
A: 

You need to cast, you actually need to know the actual type, unless it doesn't make sense.

DoStuffWithTable<MyType>((Table<MyType>)tables["my table"]);

You should consider to make the method not generic if you want to call it without knowing the actual type.

Stefan Steinegger
+1 agreed, if you can't do that (which doesn't makes much sense anyway :P), just make DoStuffWithTable non generic and work with ITable
eglasius
+1  A: 

The problem is that you don't know the type at compile-time - which is what generics is tailored for.

To call a generic method where you only know the type argument at execution time, you basically need reflection - get the generic method, call MakeGenericMethod and then invoke the returned method.

Jon Skeet
Yes, generics are not the solution to this problem, polymorphism may well be the answer you're looking for.
Binary Worrier
I feared as much, but the generic method is in a third part library so there is no way to avoid it.
Morten Christiansen
A: 

There is a misunderstanding here between generics and polymorphism. Generally, generics deal with things of a single type where the type is defined at compile time*, whereas polymorphism is about things of different types that exhibit common functionality defined as an interface or base type.

You seem to be trying to create a polymorphic type (things of different type that exhibit the same behaviour) where each polymorphic instance is defined by a generic type.

So, to update your code:

interface ITable
{
   void SomeCommonFunction ();
}

class Table<T> : ITable
{
   void SomeCommonFunction () { do something - T is known at compile time! }
}

class Program
{
   static void Main(string[] args)
   {
      var tables = new Dictionary<string, ITable>();
      ... //insert tables
      tables["my table"].SomeCommonFunction ();
   }
}

Now, if you want to do different things in SomeCommonFunction that is dependant on the type T, then you want to have specific instantiations of the Table type. C# doesn't allow for specialisations of generic type in the way that C++ can with its templates so you'll have to do:

class TableOfInt : ITable
{
   void SomeCommonFunction () { do something different! }
}

Skizz

* You can define the type at run time in C# but that's getting into crazy reflection territory.

Skizz