您将如何确定是否为变量类型(不是预先知道的类型)实现了泛型类型接口? 使用抽象基类和接口的零反射

我有一个实现通用接口的对象数组,例如

public class BaseEvent {}

public interface iResponder<in T> where T: BaseEvent
{
    void respondToEvent(T event)
}

还有一些实现,例如

public class UpEvent : BaseEvent {}

public class DownEvent : BaseEvent {}

public class UpResponder : iResponder<UpEvent> {}
{
    respondToEvent(UpEvent event) {}
}
public class DownResponder: iResponder<DownEvent> {}
{
    respondToEvent(DownEvent event) {}
}

如果我创建一个对象数组,这些对象以不同的方式实现这些接口(所以一堆 UpResponders 和 DownResponders)。我还有一个向上或向下的事件列表,并在数组中运行。我可以使用

查询特定接口是否可用
//pre-existing
List<BaseEvent> myEventsList;
List<object> myRespondersList;

foreach (BaseEvent nextEvent in myEventsList) 
{
    foreach (nextResponder in myRespondersList) 
    {
        if (nextResponder is iResponder<UpEvent>)
        {
            iResponder<UpEvent> castResponder = nextResponder as iResponder<UpEvent>;
            nextResponder.respondToEvent(nextEvent);
        }
    }
}

但是,我更喜欢获取 BaseEvent 的类型(例如 UpEvent)并确定对象是否实现了接口,而不必尝试每种已知类型。例如

//pre-existing
List<BaseEvent> myEventsList;
List<object> myRespondersList;

foreach (BaseEvent nextEvent in myEventsList) 
{
    // get the type here
    System.Type eventType = nextEvent.GetType();
    foreach (nextResponder in myRespondersList) 
    {
        // eventType cannot be used to define the interface required 
        if (nextResponder is iResponder<eventType>)  // - this will not work
        {
            iResponder<eventType> castResponder = nextResponder as iResponder<eventType>;
            nextResponder.respondToEvent(nextEvent);
        }
    }
}

我已经找到了识别对象是否实现通用接口和其他技巧的方法,但我无法拼凑起来使这项工作发挥作用。

我曾尝试专门为每种侦听器类型创建列表(这将为我正在做的事情提供更好的性能)但这只是解决了问题 - 在某些时候我需要查看是否为某个对象实现了接口我只知道运行时的类型。

非常感谢任何帮助

haofanawori 回答:您将如何确定是否为变量类型(不是预先知道的类型)实现了泛型类型接口? 使用抽象基类和接口的零反射

一种可能的选择是使用可以报告类型是否被处理的非通用接口。

public interface iResponder
{
    void respondToEvent(BaseEvent event)
    bool supportsEvent(BaseEvent event)
}
public interface iResponder<in T> : iResponder where T: BaseEvent
{
    void respondsToEvent(T event)
}

然后使用显式接口实现来实现类。

public class UpResponder : iResponder<UpEvent>
{
    public bool respondsToEvent(BaseEvent event) {
        return event is UpEvent;
    }

    void iResponder.respondToEvent(BaseEvent event) {
        respondToEvent((UpEvent)event);
    }

    public void respondToEvent(UpEvent event) {}
}
public class DownResponder : iResponder<DownEvent>
{
    public bool respondsToEvent(BaseEvent event) {
        return event is DownEvent;
    }

    void iResponder.respondToEvent(BaseEvent event) {
        respondToEvent((DownEvent)event);
    }

    public void respondToEvent(DownEvent event) {}
}

让你的函数看起来像

//pre-existing
List<BaseEvent> myEventsList;
List<iResponder> myRespondersList;

foreach (BaseEvent nextEvent in myEventsList) 
{
    foreach (iResponder nextResponder in myRespondersList) 
    {
        if (nextResponder.respondsToEvent(nextEvent)) {
            nextResponder.respondToEvent(nextEvent);
        }
    }
}

您也可以删除 RespondsToEvent 函数,并将 is 检查放在显式方法中,如果类型检查失败,则根本不调用派生版本。

,

Type 类有一个方法 GetGenericArguments,该方法将返回用于特化泛型类型的类型数组,让您过滤与提供的 eventType 匹配:

foreach (var responder in myResponderList)
{
    var typeArgument = responder.GetType().GetGenericTypeArguments().FirstOrDefault();
    if (typeArgument == eventType)
    {
        // dynamic invoke via reflection - a few lines of code
    }
}

或者,您可以获取 MethodInfo 方法的 RespondTo 并检查参数类型。无论如何你都必须得到它才能进行动态调用,所以不妨直接去那里:

var method = responder.GetType().GetMethod("RespondTo",BindingFlags.Public | BindingFlags.Instance);
var expectedType = method.GetParameters().FirstOrDefault()?.ParameterType;
var eventType = nextEvent.GetType();
if (expectedType.IsAssignableFrom(eventType))
    method.Invoke(responder,new[] { nextEvent });

我们可以将所有这些都包装在一个非通用的父接口中,使代码更加分隔。但是,它不会更快地停止反射和动态调用。

理想情况下,您希望尽可能少地进行反思。如果您无法避免它,那么您应该尝试缓存结果,以便您不必经常这样做。或者找到一种无需反射即可工作的方法,最好无需大量工作。

使用抽象基类和接口的零反射

让我们添加一个非通用接口并将其用作通用接口的基础,如下所示:

interface iResponder
{
    void RespondTo(BaseEvent evt);
}

interface iResponder<T> : iResponder
    where T : BaseEvent
{
    void RespondTo(T evt);
}

现在我们可以实现一个抽象基类来处理类型过滤:

abstract class ResponderBase<T> : iResponder<T>
{
    public void RespondTo(BaseEvent evt)
    {
        if (evt is T realEvent)
            RespondTo(realEvent);
    }

    public abstract void RespondTo(T evt);
}

最后,您的响应者类现在派生自该基础:

class UpResponder : ResponderBase<UpEvent>
{
    public override void RespondTo(UpEvent evt)
    {
        // do something.
    }
}

以这种方式组织代码意味着永远不必进行反射。你的调度循环变成:

List<BaseEvent> myEventsList;
List<iResponder> myRespondersList;

foreach (BaseEvent event in myEventsList)
{
    foreach (iResponder responder in myRespondersList)
    {
        responder.RespondTo(event);
    }
}

由于我们使用了接口,您可以在外部库中重新实现基类,或者在接口的具体实现中以不同的方式处理事情。

,

用反射处理泛型通常很烦人而且很复杂。如果您想使用反射,可能是因为您不想引入新界面(如其他答案)。我发现将所有泛型处理包装到一个方法中,然后使用反射来调用该方法更容易。

例如;

public void ProcessEvent<T>(T eventObj) where T : BaseEvent
{
    List<object> list = null; // TODO
    foreach (var handler in list)
    {
        if (handler is iResponder<T> responder)
            responder.respondToEvent(eventObj);
    }
}

public void ProcessBaseEvent(BaseEvent eventObj)
{
    var method = new Action<BaseEvent>(ProcessEvent).Method
        .GetGenericMethodDefinition()
        .MakeGenericMethod(eventObj.GetType());
    method.Invoke(this,new object[] { eventObj });
}

本文链接:https://www.f2er.com/430.html

大家都在问