将通用类型转换为其基类型和相反操作

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的答案改变了代码,它正在工作。但是,为什么字典不能持有所有响应消息的基类?有没有方法可以实现这一点,或者我只是试图让自己的头撞在墙上?

编辑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(基础)类型一直转换到具体类型,即 SystemResetResponse,以便 MessageBinder 中的 Forwarder 可以找到这个消息类型的订阅者并将其转发。
有很多类型,手动转换会过度繁琐...是否有其他方法解决这个问题,或者设计这种类型系统的更好方法?
编辑4:
我像这样更改了代码...这是正确的做法吗?是否有其他更好的方法,总之,我是在尝试以正确的方式解决问题,还是有更好的处理方式?
private void OnAsyncResponseReceived(Response response)
{
    dynamic resolvedResponse = Convert.ChangeType(response, response.GetType());
    messageBinder.Forward(resolvedResponse);
}
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;
    }

希望能对您有所帮助!

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?


我忘了提到Response和AFResponse是抽象类,因此它永远不会是那种类型。类层次结构如下:Response -> AFResponse -> AF层消息的具体实现 - DavorinP

2

你遇到的问题之一是,你试图将字典声明为具有通用订阅者(List<Subscriber<Response>>),而期望每个条目是不相关的类型(List<Subscriber<TResponse>>)。解决方案是将实际类型隐藏在objectIList后面:

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>不相关,即使stringint都继承自object

你要找的是叫做协变性,只有一些接口支持。

例如,如果你有:

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>>仍然是不相关的类型。


谢谢你的回答! :) 但是你能解释一下为什么字典不能持有所有消息的基类,因为Response是所有响应的基类吗? - DavorinP
我已经尝试使用协变性,但正如你所说,它在我的情况下不适用,或者说它适用吗?现在我面临另一个问题...在运行时如何将响应转换为其具体类型...我在编辑#3中描述了问题。谢谢! - DavorinP
不,协变不适用于您的情况。至于其他问题,我认为您最好将其作为不同的问题发布。 - Bojan Resnik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接