Convert a generic type to its base and vice versa

Can someone help me with the conversion that I came across in the attached code ... I commented out the lines of code where I have a problem. This is even the right way to achieve this ... what I'm trying to do is respond the specified type to the provided callback.

EDIT 1

I forgot to mention that Response and AFResponse are abstract classes that goes:> Response -> AFResponse -> concrete implementations of AF level messages

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; } } 

Thanks for any help :)

EDIT 2

I changed the code according to @Bojan's answer and it works. But I'm curious why the Dictionary cannot contain the base class of all Response Messages? Is there a way to do this, or am I just trying to push my head through the wall?

EDIT 3

Now I am faced with another problem ... when the message arrives, it consists of a byte array> that goes into the factory message, which resolves and builds it:

 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}); } 

Then the OnAsyncResponseReceived (Response response) event handler, which then passes the message to the subscriber of this message, if any. The problem is that if I subscribe to (sub layer response: AFResponse, SystemResponse, etc.) SystemResetResponse, which is a subclass of SystemResponse, which is a subclass of Response, I have to drop this answer from the Response type (base) to a specific type, which SystemResetResponse so that the Forwarder in MessageBinder can find subscribers to this type of message and forwards it.

There are many types, and manual casting will be redundant ... is there a way around this, or even a better way to develop this type of system?

EDIT 4

I changed code like this ... is this the right way to do this, is there any other, better way and over everything, am I trying to solve a problem in the right direction or is there a better way to handle this?

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

Part of your problem is that you are trying to declare your dictionary as having shared subscribers ( List<Subscriber<Response>> ), expecting each entry to be of an unrelated type ( List<Subscriber<TResponse>> ). The solution would be to hide the actual type behind object or 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; } 

Your Forward method may simplify checking AFResponse and subclasses a bit, but the cast should go through 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

You have not announced that your dictionary contains a “base class of all messages”. Even if Response is the base class of all responses, Subscriber<Response> and Subscriber<T> are unrelated types. This is somewhat similar to how List<int> and List<string> not related, although both string and int are inherited from object .

What you are looking for is called covariance and is only supported by some interfaces.

For example, if you have:

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

You can do:

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

However, List<ISubscriber<Response>> and List<ISubscriber<AFResponse>> will still be unrelated types.

+2
source

You need to pass each item in subscribers to the Subscriber<TResponse> list. Something like this always works for me -

 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; } 

Hope this helps!

+2
source

Like the answer already provided, the following lines of code ...

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

You can replace either:

  if (response is AFResponse) 

or (better)

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

EDIT Updated code because I did not know in advance that TResponse was shared. Do you also know that IsSubclassOf returns false if the appropriate type?

+2
source

All Articles