In C#, I have a function that passes in T
using generics
and I want to run a check to see if T
is an object
that implements a
If you have a generic-type parameter that may or may not implement IFoo
, it's possible to as
cast it to a storage location of type IFoo
; if you do that, you may pass it to any method which expects an IFoo
, as well as any method that expects a generic parameter constrained to IFoo
, but you will lose all generic type information if you do that--the parameter will be passed as type IFoo
. Among other things, this will mean that if your original object was a structure, it will be boxed.
If you wish to test whether a generic parameter type implements IFoo
and call a method that takes a generic-constrained IFoo
if it does, while keeping the original generic type (this could be useful if the type is a struct, and could be necessary if the type is being passed to a generic method which has both IFoo
and IBar
constraints and the things one might want to pass do not share any single common supertype), it will be necessary to use Reflection.
For example, suppose one wants to have a method Zap
which takes a generic ref
parameter, calls Dispose
on it if it implements IDisposable
, and clears it out. If the parameter is an IDisposable
class type, a null test should be performed as an atomic operation with clearing out the parameter.
public static class MaybeDisposer
{
static class ClassDisposer where T : class,IDisposable
{
public static void Zap(ref T it)
{
T old_it = System.Threading.Interlocked.Exchange(ref it, null);
if (old_it != null)
{
Console.WriteLine("Disposing class {0}", typeof(T));
old_it.Dispose();
}
else
Console.WriteLine("Class ref {0} already null", typeof(T));
}
}
static class StructDisposer where T : struct,IDisposable
{
public static void Zap(ref T it)
{
Console.WriteLine("Disposing struct {0}", typeof(T));
it.Dispose();
it = default(T);
}
}
static class nonDisposer
{
public static void Zap(ref T it)
{
Console.WriteLine("Type {0} is not disposable", typeof(T));
it = default(T);
}
}
class findDisposer
{
public static ActByRef Zap = InitZap;
public static void InitZap(ref T it)
{
Type[] types = {typeof(T)};
Type t;
if (!(typeof(IDisposable).IsAssignableFrom(typeof(T))))
t = typeof(MaybeDisposer.nonDisposer<>).MakeGenericType(types);
else if (typeof(T).IsValueType)
t = typeof(MaybeDisposer.StructDisposer<>).MakeGenericType(types);
else
t = typeof(MaybeDisposer.ClassDisposer<>).MakeGenericType(types);
Console.WriteLine("Assigning disposer {0}", t);
Zap = (ActByRef)Delegate.CreateDelegate(typeof(ActByRef), t, "Zap");
Zap(ref it);
}
}
public static void Zap(ref T it)
{
findDisposer.Zap(ref it);
}
}
The first time the code is invoked with any type T
, it will determine what sort of generic static class can be produced for that parameter and use Reflection to create a delegate to call a static method of that generic class. Subsequent calls with the same type T
will use the cached delegate. Even though Reflection can be a little slow, it will only need to be used once for any type T
. All subsequent calls to MaybeDisposer.Zap
with the same type T
will be dispatched directly through the delegate and will thus execute quickly.
Note that the calls to MakeGenericType
will throw exceptions if they are given generic type parameters which do not meet the constraints of the given open generic classes (e.g. if T
was a class or did not implement IDisposable
, an attempt to make the generic type StructDisposer
would throw an exception); such tests occur at run-time and are not validated by the compiler, so you can use run-time checking to see if the types meet appropriate constraints. Note that the code does not test whether it
implements IDisposable
, but instead tests whether T
does. This is very important. Otherwise, if MaybeDispose
was called with a parameter of type Object
which held a reference to a Stream
, it would determine that it
implemented IDisposable
and thus try to create a ClassDisposer
, crashing because Object
does not implement IDisposable
.