【问题标题】:Dynamic generic interface cast动态通用接口转换
【发布时间】:2011-04-08 09:09:19
【问题描述】:

我想动态发现和注册接口实现。为了论证,我有两种类似的方法:

public void Register<TEvent>(IHandler<TEvent> handler) where TEvent : IEvent

public void Register<TEvent>(Action<TEvent> action) where TEvent : IEvent
{
    Register<TEvent>(handler.Handle);
}

接口如下:

public interface IHandler<T> where T : IEvent
{
    void Handle(T args);
}

public interface IEvent
{
}

然后我有具体的实现,例如:

public class ChangedEvent : IEvent
{...}

public class ChangedHandler : IHandler<ChangedEvent>
{
    public void Handle(ChangedEvent args)
    {

    }
}

然后我可以在我的程序集中发现 IHandler 的所有具体实现,我想做这样的事情:

IList<Type> types = TypeFinder.GetImplementors(typeof(IHandler<>));
foreach (Type type in types)
{
    object instance = Activator.CreateInstance(type);
    Listeners.Register((IHandler<IEvent>)instance);
}

代码将编译,它不是无效的,但在运行时转换失败,因为它是无效的。 但是,如果我转换为具体的 IEvent,例如:

IList<Type> types = TypeFinder.GetImplementors(typeof(IHandler<>));
foreach (Type type in types)
{
    object instance = Activator.CreateInstance(type);
    Listeners.Register((IHandler<ChangedEvent>)instance);
}

这个演员表是有效的,它将运行。问题是场景的动态,我希望能够发现类型并注册它们。 我不想为处理程序创建一个非通用接口,但我相信这是一个不可能的场景,因为框架没有足够的信息来推断所需的类型。 有什么方法可以实现这一点,或者您有什么建议可以达到预期的效果吗?

非常感谢。

【问题讨论】:

  • ChangedEvent 根本不继承自 IHandler&lt;IEvent&gt;,而仅继承自 IEvent,即使可以从非泛型向下转换为泛型,您的演员阵容也不会起作用,或者我缺少什么?
  • 你是绝对正确的。非常抱歉,我没有包含处理程序实现的代码。我已经更新了问题。

标签: c# generics reflection casting


【解决方案1】:

由于Covariance and contravariance,这不起作用,想象一下它起作用并且您执行了以下操作。

public class AnotherTypeOfEvent : IEvent      {...} 

public void Register<TEvent>(IHandler<TEvent> handler) where TEvent : IEvent 
{
    //Really our runtime type expects ChangedEvent, but our constraint is
    //only for IEvent so you could do this - oh dear..
    handler.Handle(new AnotherTypeOfEvent());     
}

Listeners.Register((IHandler<IEvent>)new ChangedHandler());   

您会将AnotherTypeOfEvent 传递给您的ChangedHandler.Handle 方法,它显然期望ChangedEvent,这会导致各种问题。

【讨论】:

    【解决方案2】:

    你为什么不在 typefinder 中指定你想得到的类型,因为你会在之后转换它?

    IList<Type> types = TypeFinder.GetImplementors(typeof(IHandler<IEvent>));
    foreach (Type type in types)
    {
        object instance = Activator.CreateInstance(type);
        Listeners.Register((IHandler<IEvent>)instance);
    }
    

    【讨论】:

    • 嗨,mathieu,感谢您的回答,但它失败了。问题是返回的类型不是通用的,例如不能将 ChangeEvent 类创建为通用实例,因为类型定义不是通用的。问题是从一个具体的实现到一个通用的。如果返回的类型是 List 之类的泛型,但不是非泛型类型,则可以这样做。
    【解决方案3】:

    我不确定您遇到了什么错误。这对我来说编译得很好:

    public interface IEvent
    {
    
    }
    
    public interface IHandler<T>
    {
    
    }
    
    public class Test //: ITest
    {
        public void Register<TEvent>(IHandler<TEvent> handler) where TEvent : IEvent
        {
    
        }
    }
    
    public class ChangedEvent : IEvent
    {
    
    
    }
    public class Example
    {
        public static void Main()
        {
            Test t = new Test();
            Type[] types = new Type[10];
            foreach (Type type in types)
            {
                object instance = Activator.CreateInstance(type);
                t.Register((IHandler<IEvent>)instance);
            }
        }
    }
    

    【讨论】:

    • 是的,它可以编译。我没有说它不会编译,抱歉我需要更新我的问题。它会编译,但在运行类型中你会得到一个无效的强制转换异常。
    • 引发运行时异常的实例类型是什么?
    • t.Register((IHandler)instance);从我的例子中: object instance = Activator.CreateInstance(type);返回一个 ChangedEvent 的实例然后这个转换将失败:Listeners.Register((IHandler)instance);无法再次将实现强制转换为泛型类型。
    • 不!我的意思是它失败的 instance 对象的运行时类型是什么。
    • 在这种情况下ChangedEvent的实例会导致错误。
    猜你喜欢
    • 2012-08-28
    • 1970-01-01
    • 1970-01-01
    • 2014-10-13
    • 2020-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-03-18
    相关资源
    最近更新 更多