【问题标题】:Dynamically resolve and cast base to derived type at runtime在运行时动态解析并将基转换为派生类型
【发布时间】:2012-07-06 11:21:36
【问题描述】:

如何在运行时将基类转换为派生类。 我想做的是以下几点:我需要一个可以保存订阅的系统, 其中某种类型的消息具有指定的订阅者。当收到一条消息时,它会双向转发到该系统,该系统将查找是否有任何此类消息的订阅者,如果有,则将消息转发给订阅者。我有一个名为 Response 的抽象基类,然后是一个抽象层,例如。 AFResponse :响应,然后是实际(具体)实现,例如。 响应(抽象):AFResponse(抽象):AFIncommingMessage(具体)。

收到数据包后,它会转到 MessageBuilder,它会解析消息类型并构建它,如下所示:

public static T Build<T>(Packet packet) where T : Response
{
    Type resolvedType;
    if (!dependencyMap.TryGetValue(packet.MessageId, out resolvedType))
    {
        var str = String.Format("Could not resolve message. Message info: CMD0: {0}, CMD1: {1}, MessageID: {2}",
                                packet.Cmd0, packet.Cmd1, packet.MessageId);
        Debug.WriteLine(str);

        throw new ResolutionFailedException(str);
    }

    ConstructorInfo firstConstructor = resolvedType.GetConstructors().First();

    return  (T) firstConstructor.Invoke(new object[] {packet});
}

然后如果消息是异步的,它会被转发到MessageBinder,它持有消息类型的订阅。

private void OnAsyncResponseReceived(Response response)
{
    messageBinder.Forward(response);
}

MessageBinder 类实现如下:

public class MessageBinder
{
    private class Subscriber<T> : IEquatable<Subscriber<T>> where T : Response
    {
        ...
    }

    private readonly Dictionary<Type, IEnumerable> bindings;

    public MessageBinder()
    {
        this.bindings = new Dictionary<Type, IEnumerable>();
    }

    public void Bind<TResponse>(ushort shortAddress, Action<ZigbeeAsyncResponse<TResponse>> callback)
        where TResponse : Response
    {
        HashSet<Subscriber<TResponse>> subscribers = this.GetSubscribers<TResponse>();
        if (subscribers != null)
        {
            subscribers.Add(new Subscriber<TResponse>(shortAddress, callback));
        }
        else
        {
            var subscriber = new Subscriber<TResponse>(shortAddress, callback);
            this.bindings.Add(typeof(TResponse), new HashSet<Subscriber<TResponse>> { subscriber });
        }
    }

    public void Forward<TResponse>(TResponse response)
        where TResponse : Response
    {
        var subscribers = this.GetSubscribers<TResponse>();
        if (subscribers != null)
        {
            Subscriber<TResponse> subscriber;

            var afResponse = response as AFResponse;
            if (afResponse != null)
            {
                subscriber = subscribers.SingleOrDefault(s => s.ShortAddress == afResponse.ShortAddress);
            }
            else
            {
                subscriber = subscribers.FirstOrDefault();
            }

            if (subscriber != null)
            {
                Debug.WriteLine("Forwarding received async response of type " + response.GetType().Name);
                subscriber.Forward(response);
            }
        }
    }

    private HashSet<Subscriber<TResponse>> GetSubscribers<TResponse>() where TResponse : Response
    {
        IEnumerable subscribers;
        this.bindings.TryGetValue(typeof(TResponse), out subscribers);

        return (HashSet<Subscriber<TResponse>>)subscribers;
    }
}

当调用 MessageBinder 类的 GetSubscribers() 方法时会出现问题,因为 TResponse 的类型是基类的类型,即 Response,因此找不到订阅者。所以我认为我需要做的是以某种方式将消息的实际类型传递给 Forward 方法,这样该类型就是响应的实际类型。

我改变了这样的方法:

private void OnAsyncResponseReceived(Response response)
{
    messageBinder.Forward((dynamic)response);
}

它确实有效,但不知何故我觉得有更好的(?)方法来做到这一点......或者这是实际且唯一的解决方案?也许我应该改变整体设计,我不知道......我第一次遇到这种类型的问题,这就是我想出的。

我愿意接受建议、批评,当然还有最好的解决方案 :) 我很好奇,如果它与我的解决方案不同且更有效,您将如何实现它。 感谢您的帮助!

【问题讨论】:

  • 从孩子到父母的转换很容易,但父母到孩子是不可能的。例如,将 Textbox 转换为 Control 是可能的,但 Control 转换为 TextBox 是不可能的。
  • @NikhilAgrawal:您的意思是不可能转换为派生类?
  • 隐式否。显式是。阅读此stackoverflow.com/questions/124336/…
  • @Nikhil Agrawal 我要的是这个,正如 Maciej Hehl 在您提供的链接中所写:link

标签: c# dynamic casting base derived


【解决方案1】:

您是否考虑过通过消息本身路由订阅者请求?它不一定需要是直接的 - 您的基类可能需要子类来覆盖公开某种实用程序对象的属性。实用程序对象可以是消息类型的强类型,并且能够进行强类型调用以获取订阅者。

您仍然可以使用您创建的方法(用于获取订阅者) - 实用程序类将能够进行强类型调用。

编辑:

这个怎么样?

private void OnAsyncResponseReceived<T>(T response) where T : Response {
  messageBinder.Forward(response);
}

原始代码的问题在于响应变量是强类型的。转换为动态的“非强类型”它。通过使用泛型方法,就像您在整个应用程序中所做的那样,加上泛型类型必须从 Response 继承的附加约束,您可以有效地获得相同的结果,但具有内置的动态类型。不过,老实说,我认为转换为动态是相当聪明的。 :)

【讨论】:

  • 确实可行……没想到。谢谢你的提示!但这是处理此类问题的唯一方法,还是有更有效的设计方法?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-14
  • 2016-11-06
  • 2017-01-08
  • 2013-11-17
相关资源
最近更新 更多