Call method on object on which generic type is not known except at runtime? Should I just use a dynamic?

204 views Asked by At

say I have a class

public abstract class A<T>
{
     public T GetT{get;}

     public ISomeInterface AMethod{get;}
}

Then I have some other method in another class where I take an object and I want to check it is of type A<> then if it is get the type of T and call the method AMethod. So I'm doing this:

if (theObject.GetType().GetGenericTypeDefinition() == typeof (A<>))
{
    Type TType = theObject.GetType().GetGenericArguments()[0];            
    dynamic dynamicObject= theObject;
    ISomeInterface filter = dynamicObject.AMethod;
    //...some other stuff using TType
} 

Is there a way to do this without using the dynamic object, since I can't declare the type of the variable using the TType or using the generic type defintion A<> at runtime...

2

There are 2 answers

2
Jon Skeet On BEST ANSWER

If you're able to, put all the non-generic stuff in an abstract non-generic base class:

public abstract class A
{
     public ISomeInterface AMethod{get;}
}

public abstract class A<T> : A
{
     public T GetT{get;}
}

Then you can just use:

A foo = theObject as A;
if (foo != null)
{
    ISomeInterface filter = foo.AMethod;
}
0
supercat On

As mentioned by Mr. Skeet, pulling items which don't depend on the class type into a non-generic class or interface is generally the way to go when practical. Otherwise, I would suggest writing a generic method with type parameter T which will return a generic singleton (generated via Reflection) that can pass an object to a generic method with parameter types U such that T:A<U>. Such a method would only have to use Reflection once for any particular type T, no matter how many times it was used to handle instances of that type.

Because delegates are only usable with closed generics, one would probably have to define some generic interfaces:

// User code will supply an implementation of one of these interfaces to the dispatching
// object, which will call its "Act" method with a proper type
interface IActUponGenericA
{
  void Act<T>(A<T> param);
}
interface IActUponGenericA<PT1>
{
  void Act<T>(A<T> param, PT1 extraParam1);
}
interface IActUponGenericA<PT1,PT2>
{
  void Act<T>(A<T> param, PT1 extraParam1, PT2 extraParam2);
}
// The dispatching object itself will implement this interface:
interface IWrapActUponGenericA<T>
{
  void CallAction(IActUponGenericA act, T param);
  void CallAction<PT1>(IActUponGenericA<PT1> act, T param, PT1 extraParam1);
  void CallAction<PT1,PT2>(IActUponGenericA<PT1,PT2> act, T param, 
                             PT1 extraParam1, PT2 extraParam2);
}

Then given a parameter type T (which implements A<Q> for some Q), the Reflection-based method code will generate a singleton which implements interface IWrapActUponGenericA<T>. That implementation can take an object of type T, and an implementation of IActUponGenericA, and call that implementation's Act<Q> method.

This approach will be more complicated than using dynamic, and may or may not perform better; it will, however, be able to deal with some situations that dynamic can't.