Call method on object on which generic type is not known except at runtime? Should I just use a dynamic?
-
12-06-2021 - |
Domanda
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...
Soluzione
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;
}
Altri suggerimenti
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.