In C# how can i check if T is of type IInterface and cast to that if my object supports that interface?

前端 未结 8 1349
粉色の甜心
粉色の甜心 2021-02-05 10:52

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

8条回答
  •  暖寄归人
    2021-02-05 11:26

    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(ref T it) 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.

    提交回复
    热议问题