An assembly can define, within the same namespace, multiple types with the same name, as long as the number of type arguments (generic parameters) differs for each type. Try for example:
var strA = typeof(Action).ToString(); // "System.Action"
var strB = typeof(Action<>).ToString(); // "System.Action`1[T]"
var strC = typeof(Action<,>).ToString(); // "System.Action`2[T1,T2]"
var strD = typeof(Action<,,>).ToString(); // "System.Action`3[T1,T2,T3]"
In C#, one can only use empty <...>
, i.e. avoid specifying what the type parameters are, within the typeof(...)
keyword.
Another example: typeof(Dictionary<,>).ToString()
gives "System.Collections.Generic.Dictionary`2[TKey,TValue]"
. As you can see, in .NET (the CLI) the number of generic parameters is given by appending a backtick followed by digit(s).
Examples of use:
static bool IsDictionary(object obj)
{
if (obj == null)
return false;
var t = obj.GetType();
return t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Dictionary<,>);
}
static object GetDictionary(object a, object b)
{
var t = typeof(Dictionary<,>).MakeGenericType(a.GetType(), b.GetType());
return Activator.CreateInstance(t);
}
But if possible, avoid reflection and use compile-time types, for example (not quite equivalent to the above):
static Dictionary<TA, TB> GetDictionary<TA, TB>(TA a, TB b)
{
return new Dictionary<TA, TB>();
}