将泛型类型转换为其基础,反之亦然

时间:2012-07-05 02:39:12

标签: c# generics casting generic-list

有人可以帮我解决我在封闭代码中遇到的转换问题吗...我评论了代码行,我遇到了问题。这是否是实现这一目标的正确方法......我正在尝试做的是指定类型的前向响应以提供回调。

  

编辑1

     

我忘了提及Response和AFResponse是抽象类,   其中:>响应 - > 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无法保持所有的基类   回复消息?有没有办法实现这一点,或者我   我试着把头伸进墙里?

     

编辑3

     

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

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(响应响应)事件处理程序   被调用,然后将消息转发给订阅者   消息,如果有的话。现在的问题是,如果我订阅(子层   响应是:AFResponse,SystemResponse等...)   SystemResetResponse是SystemResponse的子类,它是   Response的子类,我必须从中转发响应   响应(基础)类型一直到具体类型,即   SystemResetResponse按顺序在MessageBinder中的Forwarder可以找到   此消息类型的订阅者,并转发它。

     

有许多类型和手工铸造将是一个矫枉过正的...是   有办法解决这个问题,甚至是设计这种类型的更好方法   系统

     

编辑4

     

我改变了这样的代码......这是正确的方法吗,是   还有其他,更好的方式,而且我还在努力解决问题   问题是正确的方法还是有更好的方法来解决这个问题?

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

3 个答案:

答案 0 :(得分: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;
    }

希望它有所帮助!!

答案 1 :(得分: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?

答案 2 :(得分: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

您要查找的内容名为covariance,仅受某些界面支持。

例如,如果你有:

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