4

有人可以帮助我解决我在封闭代码中面临的转换......我评论了我遇到问题的代码行。这甚至是实现这一目标的正确方法吗……我想做的是将指定类型的响应转发到提供的回调。

编辑 1

我忘了提到 Response 和 AFResponse 是抽象类,它们是:>Response -> AFResponse -> AF 层消息的具体实现

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

    private readonly Dictionary<Type, List<Subscriber<Response>>> bindings;

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

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

            // ERROR: cannot convert from 'List<Subscriber<TResponse>>' to 'List<Subscriber<Response>>' ... tried LINQ Cast operator - does not work either
            this.bindings.Add(typeof(TResponse), new List<Subscriber<TResponse>> { subscriber });
        }
    }

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

            if (responseType.IsSubclassOf(typeof (AFResponse)))
            {
                // ERROR: Cannot convert type 'TResponse' to 'AFResponse' ... tried cast to object first, works, but is this the right way?
                var afResponse = (AFResponse)response;
                subscriber = subscribers.SingleOrDefault(s => s.ShortAddress == afResponse.ShortAddress);
            }
            else
            {
                subscriber = subscribers.First();
            }

            if (subscriber != null)
            {
                subscriber.Forward(response);
            }
        }
    }

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

        // ERROR: How can I cast List<Subscriber<Response>> to List<Subscriber<TResponse>>?
        return subscribers;
    }
}

感谢您的任何帮助 :)

编辑 2

我根据@Bojan 的回答更改了代码,它正在工作。但我很好奇,为什么 Dictionary 不能容纳所有 Response 消息的基类?有没有办法做到这一点,或者我只是想把头推过墙?

编辑 3

现在我面临另一个问题......当消息到达时,它由字节数组组成,>它进入解决并构建它的消息工厂:

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 MessageNotFoundException(str);
    }

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

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

然后调用OnAsyncResponseReceived(Response response)事件处理程序,然后将消息转发给该消息的订阅者(如果有)。现在的问题是,如果我订阅(响应的子层是:AFResponse、SystemResponse 等...) SystemResetResponse 是 SystemResponse 的子类,它是 Response 的子类,我必须从 Response(base) 类型中转换该响应具体类型 SystemResetResponse 的方式是为了让 MessageBinder 中的 Forwarder 可以找到该消息类型的订阅者,并转发它。

有很多类型,手动铸造会有点过分……有没有办法解决这个问题,或者甚至是更好的方法来设计这种类型的系统?

编辑 4

我更改了这样的代码......这是正确的方法吗?还有其他更好的方法吗?总的来说,我是在尝试以正确的方式解决问题还是有更好的方法来处理这个问题?

private void OnAsyncResponseReceived(Response response)
{
    dynamic resolvedResponse = Convert.ChangeType(response, response.GetType());
    messageBinder.Forward(resolvedResponse);
}
4

3 回答 3

2

您需要将subscribers列表中的每个项目转换为Subscriber<TResponse>. 像这样的东西总是对我有用-

private List<T> get<T>()
    {
        List<IEntity> list = new List<IEntity>();
        List<T> genericList = new List<T>();
        foreach (var item in list)
        {
            genericList.Add((T)item);
        }
        return genericList;
    }

希望能帮助到你!!

于 2012-07-05T03:04:19.940 回答
2

除了已经提供的答案之外,以下代码行...

    Type responseType = typeof (TResponse);
    if (responseType.IsSubclassOf(typeof (AFResponse)))

可以替换为:

    if (response is AFResponse)

或更好)

    AFResponse af = response as AFResponse;
    if (af != null)

编辑更新了代码,因为我事先没有意识到 TResponse 是通用的。您是否还知道 IsSubclassOf 如果它是匹配类型则返回 false ?

于 2012-07-05T03:11:34.560 回答
2

您的问题的一部分是您试图将您的字典声明为具有通用订阅者(List<Subscriber<Response>>),同时期望每个条目都是不相关的类型(List<Subscriber<TResponse>>)。一种解决方案是将实际类型隐藏在objector后面IList

private readonly Dictionary<Type, object> bindings;

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

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

您的Forward方法可以以更简单的方式检查AFResponse和子类,但强制转换必须经过object

public void Forward<TResponse>(TResponse response)
    where TResponse : Response
{
    var subscribers = 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.First();
        }

        if (subscriber != null)
        {
            subscriber.Forward(response);
        }
    }
}

更新

您没有声明您的字典包含“所有消息的基类”。即使Response是所有响应的基类,Subscriber<Response>并且Subscriber<T>是不相关的类型。它有点类似于如何List<int>List<string>不相关,即使两者都string继承intobject.

您要查找的内容称为协方差,仅受某些接口支持。

例如,如果您有:

interface ISubscriber<out T> where T:Response
{}

class Subscriber<T> : ISubscriber<T> where T:Response
{}

你可以这样做:

ISubscriber<Response> s = new Subscriber<AFResponse>();

但是,List<ISubscriber<Response>>并且List<ISubscriber<AFResponse>>仍然是不相关的类型。

于 2012-07-05T07:40:13.607 回答