2012-07-05 1 views
4

Может кто-то помочь мне с преобразованием, с которым я столкнулся в прилагаемом коде ... Я прокомментировал строки кода, где у меня проблема. Это даже правильный способ достичь этого ... то, что я пытаюсь сделать, - это ответные реакции указанного типа на предоставленный обратный вызов.Преобразование родового типа в его базу и наоборот

EDIT 1

Я забыл упомянуть, что ответ и 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; 
    } 
} 

Благодарим за любую помощь :)

EDIT 2

Я изменил код в соответствии с @Bojan ответ, и он работает. Но Мне любопытно, почему Словарь не может содержать базовый класс всех Ответных сообщений? Есть ли способ сделать это, или я только пытался протащить голову через стену?

EDIT 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), обработчик события , который затем передает сообщение подписчику этого сообщения , если таковое имеется. Проблема заключается в том, что если я подписываюсь на (подслой ответа: AFResponse, SystemResponse и т. Д.) SystemResetResponse, который является подклассом SystemResponse, который является подклассом Response, который должен ответить на этот вопрос от Response (базовый) до бетонного типа, который составляет . SystemResetResponse для того, чтобы Экспедитор в MessageBinder мог найти подписчиков этого типа сообщения и пересылать его.

Есть много типов, и литье рук будет излишним ... Есть ли способ обойти это или даже лучший способ спроектировать этот тип системы ?

EDIT 4

Я изменил код, как это ... это правильный способ сделать это, это есть любой другой, лучший путь и над всем, я пытаюсь решить проблему правильный путь или есть лучший способ справиться с этим?

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

ответ

2

Часть вашей проблемы заключается в том, что вы пытаетесь объявить свой словарь как имеющего общих подписчиков (List<Subscriber<Response>>), ожидая, что каждая запись будет иметь несвязанный тип (List<Subscriber<TResponse>>). Решение было бы скрыть фактический тип за object или 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); 
     } 
    } 
} 

UPDATE

Вы не Декабре заставьте ваш словарь удерживать «базовый класс всех сообщений». Хотя Response является базовым классом всех ответов, Subscriber<Response> и Subscriber<T> являются несвязанными типами. Это несколько аналогично тому, как List<int> и List<string> не связаны, хотя оба и int наследуют от 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>> все равно будет неродственных типов.

+0

Благодарим вас за ответ! :) Но можете ли вы объяснить мне, почему словарь не может содержать базовый класс всех сообщений, поскольку Response является базовым классом всех ответов? – DavorinP

+0

Я пробовал использовать ковариацию, но, как вы сказали, она не играет в моем случае или не делает этого? Теперь я столкнулся с другой проблемой ... как я могу отбросить ответ на конкретный тип durning runtime ... Я описываю проблему в редакции # 3. Спасибо! – DavorinP

+0

Нет, ковариация не применяется в вашем случае. Что касается другой проблемы, я думаю, вам лучше разместить ее как другой вопрос. –

2

Вы должны бросить каждый элемент в списке subscribersSubscriber<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) 

EDIT Обновлен код, потому что я не понял, и что TResponse был общим. Вы также знаете, что IsSubclassOf возвращает false, если соответствующий тип?

+0

Я забыл упомянуть, что Response и AFRsponse являются абстрактными классами, поэтому он никогда не будет такого типа. Иерархия классов: Ответ -> AFResponse -> Конкретная реализация сообщений уровня АФ – DavorinP