Reputation: 19580
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?
Upvotes: 1
Views: 682
Reputation: 71090
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! }
}
* You can define the type at run time in C# but that's getting into crazy reflection territory.
Upvotes: 0
Reputation: 1063338
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");
}
Upvotes: 2
Reputation: 1502046
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.
Upvotes: 1
Reputation: 64628
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.
Upvotes: 0