代码之家  ›  专栏  ›  技术社区  ›  TJ Rockefeller

使用where子句强制转换为通用接口

  •  0
  • TJ Rockefeller  · 技术社区  · 8 年前

    我有一个对象,如果它实现了特定类型的接口,我想调用它的某些方法。

    我正在检查下面这样的通用接口。

    foreach (var iFace in objectType.GetInterfaces())
    {
        if (iFace.IsGenericType && iFace.GetGenericTypeDefinition() == typeof(INotify<>))
        {
            //fails to compile because object isn't the correct type.
            doSomethingWithINotifyObject(notifyObject)
        }
        //do other things if different interfaces
    }
    

    我不知道该如何做的是强制转换对象,以便它是正确的调用类型 doSomethingWithINotifyObject

    本例中的方法如下所示

    private void doSomethingWithINotifyObject<T>(INotify<T> notify) where T : EventArgs, INotificationArgs
    {
        //do stuff with notification object
    }
    

    INotify接口定义为

    public interface INotify<T> where T: EventArgs, INotificationArgs
    {
        //notify stuff
    }
    

    有没有办法把我的对象 INotify<T> where T : EventArgs, INotificationArgs 不在乎T到底是什么?

    我试着做了一个如下的通用方法

    typeof(MyClass)
        .GetMethod("doSomethingWithINotifyObject")
        .MakeGenericMethod(notifyObject.GetType())
        .Invoke(this, new object[] { notifyObject });
    

    我得到以下运行时异常

    运行时异常(第13行):“Void dosomethingWithinNotifyObject”上的generiArguments[0],“MyClass+dosomethingWithinNotifyObject”违反了“T”类型的约束。

    堆栈跟踪:

    [System.Security.VerificationException:方法MyClass.doSomethingWithINotifyObject:类型参数“MyClass+doSomethingWithINotifyObject”违反了类型参数“T”的约束。] 在System.RuntimeMethodHandle.GetStubIfNeeded(runtimemethodhandleinal方法,RuntimeType declaringType,RuntimeType[]方法实例化) 在System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[]methodInstantiation)

    [System.ArgumentException:GenericArguments[0],'MyClass+doSomethingWithINotifyObject',on'Void doSomethingWithINotifyObjectT'违反了'T'类型的约束。] 在System.RuntimeType.ValidateGenericArguments(MemberInfo定义,RuntimeType[]genericArguments,异常e) 在System.Reflection.RuntimeMethodInfo.MakeGenericMethod(Type[]methodInstantiation) 在MyClass.Main()上:第13行

    我在这里有一个.NET fiddle上的例子 https://dotnetfiddle.net/ZWMJ4x

    3 回复  |  直到 8 年前
        1
  •  1
  •   Camilo Terevinto Chase R Lewis    8 年前
    typeof(MyClass)
        .GetMethod("doSomethingWithINotifyObject")
        .MakeGenericMethod(notify.GetType())
        .Invoke(null, new object[] { notify });
    

    因为你的 Notify 类不是泛型的,即使它确实实现了泛型 INotify<FooBarClass> . 你需要传递给 MakeGenericMethod 是由 :

    typeof(MyClass)
        .GetMethod("doSomethingWithINotifyObject")
        .MakeGenericMethod(iFace.GetGenericArguments())
        .Invoke(null, new object[] { notify });
    
        2
  •  1
  •   BradleyDotNET Aniss    8 年前

    不,你需要知道 T 因为否则类型系统无法检查您的参数是否是正确的类型,或者您的返回值类型是否正确等等。

    你可以通过反射来做任何事情,绕过所有这些,或者如果你 真正地 不在乎什么 T型 只是将这些方法放在一个通用接口中并转换为该接口(它们毕竟不应该包含Ts)。

        3
  •  0
  •   John Wu    8 年前

    如果没有反省 INotify 关于T是协变的。

    由于您有一个由两部分组成的类型约束,因此需要定义一个同时实现这两个约束的基类,并派生 INotify<> 从那开始上课。

    public interface INotificationArgs
    {
    }
    
    public interface INotify<out T> where T: EventArgs, INotificationArgs
    {
        void PrintName();
    }
    
    public class MyEventArgsBase : EventArgs, INotificationArgs {}
    
    public class MyEventArgs1 : MyEventArgsBase {}
    
    public class MyEventArgs2 : MyEventArgsBase {}
    
    public class MyEventArgs3 : MyEventArgsBase {}
    
    class MyClass<T> : INotify<T> where T : EventArgs, INotificationArgs
    {
        public string Name { get; set; }
    
        public MyClass(string name) { Name = name; }
    
        public void PrintName()
        {
            Console.WriteLine(Name);
        }
    }
    
    public class Program
    {
    
        private static void doSomethingWithINotifyObject<T>(INotify<T> notify) where T : EventArgs, INotificationArgs
        {
            notify.PrintName();
        }
    
        public static void Main()
        {
            object[] tests = new object []
            {
                new MyClass<MyEventArgs1>("1"),
                new MyClass<MyEventArgs2>("2"),
                new MyClass<MyEventArgs3>("3")
            };
    
            foreach (var o in tests)
            {
                var i = o as INotify<MyEventArgsBase>;
                if (i != null)
                {
                    doSomethingWithINotifyObject(i);
                }
            }
        }
    }
    

    输出:

    1
    2
    3
    

    Link to DotNetFiddle example