6

Supose i have:

class MyBase<T1, T2>{}

class MyConcreteBase<T2> : MyBase<ConcreteType1, T2>{}

class MyConcrete1 : MyConcreteBase<ConcreteType2>{}

class MyConcrete2 : MyBase<ConcreteType1, ConcreteType2>{}

How do i get types of T1 and T2 if i have instance of MyConcrete1 or MyConcrete2 or MyConcreteBase or any other instance of type derived from MyBase<T1, T2>

The way i do it now is i'm "getting up" by inheritance chain using .GetType().BaseType while BaseType.Name.StartsWith("MyBase") and then using .GetGenericArguments()

It is working, but i'm not satisfied with it, especially .StartsWith("MyBase") part.

Anyone have other suggestions?

Andrej Slivko
  • 1,236
  • 2
  • 12
  • 27

6 Answers6

3

You can walk up the inheritance hierarchy and look for a GenericType, which is constructed from BaseType<,>. Try:

var type = c.GetType();
Type baseType = type;
while(null != (baseType = baseType.BaseType)) 
{
    if (baseType.IsGenericType) 
    {
        var generic = baseType.GetGenericTypeDefinition();
        if (generic == typeof(MyBase<,>)) 
        {
            var genericTypes = baseType.GetGenericArguments();
            // genericTypes has the type argument used to construct baseType.
            break;
        }
    }
}   
driis
  • 161,458
  • 45
  • 265
  • 341
2

Yeah, don't use string parsing. You can simply use BaseType.IsAssignableFrom(typeof(MyBase<,>)) (might have to reverse the BaseType and MyBase<,> -- I always get that confused).

It might also be easier to expose the types as properties in your base class, e.g.:

public Type T1_Type { get { return typeof(T1); } }
public Type T2_Type { get { return typeof(T2); } }

I do have to ask, why do you need to extract these type parameters? This is a code smell to me.

EDIT: I should add that you can't use IsAssignableFrom as is for generics. Check out this answer for a full solution: How To Detect If Type is Another Generic Type

Community
  • 1
  • 1
siride
  • 200,666
  • 4
  • 41
  • 62
  • @siride - i need it because i have generic ReportController in my MVC app and i have action there Show(string reportName). Then i have ReportBase and then WhateverReport : ReportBase and then YearlyWhateverReport : WhateverReport... and so on. So i need to know type of TEntity to pass it to repository and get needet IQueryable to give it to ReportBase.Generate(IQueryable entityQuery) – Andrej Slivko May 30 '11 at 17:02
  • @qrow then I think exposing the types as properties in the base class is the best bet. It's also a lot faster than using reflection. – siride May 30 '11 at 17:07
  • @siride you could be right, this probably would be simplest solution, somehow i used too much of reflection for those generic actions so i didn't thought about just exposing generic arg types on base class – Andrej Slivko May 30 '11 at 17:16
  • @siride i'm back to work and i finally can test solutions suggested here. I realized that exposing types in MyBase is not sutable for me because i don't know type of derived object, and i can't do something like ((MyBase<,>)report).T1_Type ... So probably my only option is reflection after all – Andrej Slivko May 31 '11 at 09:12
  • finally i used dynamic instead of object and was able to call T1_Type. So i stick to this solution – Andrej Slivko May 31 '11 at 12:19
  • @qrow: you don't need to cast to base...all the derived classes will have those properties as well. Dynamic is probably not the best solution since it incurs overhead and reduces type safety. – siride May 31 '11 at 18:18
  • @siride i don't know type of derived class at compile time, it comes from url, in action Show(string reportName) reportName is really type name of derived report, so i take report name, then search for this type, create instace of it using Activator, and then i'm left with object or dynamic, i don't see other options. – Andrej Slivko May 31 '11 at 19:57
  • @qrow Then just use the object and then use reflection to get at those properties. You have to use reflection anyway, you might as well use it smartly. – siride Jun 01 '11 at 04:39
0

Would be good if you would tell us a bit more about what you want to achive. Guessing what you idea is, I would say: Just walk the complete inheritance chain as long as BaseTypeis not null. And then check for each type if it's generic and if yes, call GetGenericArguments. Should be a matter of a few predicates and one or two LINQ expressions.

Achim
  • 15,415
  • 15
  • 80
  • 144
0

I can't think of anything clever, so I'd probably just do this:

class MyBase<T1, T2> {
    protected Type GetT1() { return typeof(T1); }
    protected Type GetT2() { return typeof(T2); }
}
Corey Kosak
  • 2,615
  • 17
  • 13
0

Here is a function that will accept any type (assuming it has a base class with generic parameters) and return the types of that instance's base class's generic parameters:

public Type[] BaseGenericTypes<T>(T instance)
    {
        Type instanceType = instance.GetType();

        Type objectType = new object().GetType();

        while (instanceType.BaseType != objectType)
        {
            instanceType = instanceType.BaseType;
        }
        return instanceType.GetGenericArguments();
    }

The nice thing about this method is you don't need to know anything about the base class or any of its subsequent derived classes.

BenMorel
  • 34,448
  • 50
  • 182
  • 322
Jason Moore
  • 3,294
  • 15
  • 18
-1

Do you mean: Type.GetGenericArguments Method ?

Simon Mourier
  • 132,049
  • 21
  • 248
  • 298
  • @siride - I think it's still the answer to "How do i get types of T1 and T2 if i have instance of MyConcrete1 or MyConcrete2 or MyConcreteBase or any other instance of type derived from MyBase" – Simon Mourier May 30 '11 at 16:06
  • but he already knows that part. It's the other part of the algorithm that he needs help with. It's as if someone asked how to parse a mathematical expression and was having trouble with the grammar and you suggested that he use the Regex class to break things into tokens. Technically true, but not helpful for the question at hand. – siride May 30 '11 at 16:09