In C# I have
class Gen1<T1, T2> {}
class Gen2<T2>: Gen1<MyObject, T2> {}
class Ins: Gen2<MyInterface> {}
var ins = new Ins();
Type insType = ins.GetType();
How can I get MyObject
and MyInterface
from insType (with correct order?)
I tried:
List<Type> types = new List<Type>();
do
{
types.AddRange(checkType.GetGenericArguments());
if(checkType.IsGenericType)
{
Type genType = checkType.GetGenericTypeDefinition();
types.AddRange(genType.GetGenericArguments());
}
checkType = checkType.BaseType;
} while (checkType != null);
This gives MyInterface
and T2
. It did find the Gen2<T2>
type, but I have no idea how to get that MyObject
when Gen2 inherent from Gen1, and how to make the order correct too.
What you want are the generic arguments to the base-of-base type Gen1<T1, T2>
. To get those, it will be convenient to introduce the following extension method:
public static IEnumerable<Type> BaseTypesAndSelf(this Type type)
{
while (type != null)
{
yield return type;
type = type.BaseType!;
}
}
And now you will be able to do:
var types = insType.BaseTypesAndSelf()
.Where(t => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Gen1<,>))
.Select(t => t.GetGenericArguments())
.FirstOrDefault();
Resulting in [MyObject,MyInterface]
.
Or if you want the generic arguments to the topmost generic base type whatever it is, you could introduce the following additional extension method:
// Returns the generic arguments of the topmost base type which is generic, or an empty array if there is none.
public static Type [] GetTopmostBaseTypeGenericArguments(this Type type) =>
type.BaseTypesAndSelf()
.Where(t => t.IsGenericType)
.Reverse()
.Select(t => t.GetGenericArguments())
.FirstOrDefault() ?? Array.Empty<Type>();
And then do
var types = insType.GetTopmostBaseTypeGenericArguments();
Or if you have e.g.
class MyKeyObjectDictionary<TKey> : Dictionary<TKey, MyObject> where TKey : notnull;
class MyStringObjectDictionary : MyKeyObjectDictionary<string>;
Then
var types = typeof(MyStringObjectDictionary).GetTopmostBaseTypeGenericArguments();
will return [System.String,MyObject]
.
Demo here.