I have a piece of code like this:
var senders = new List<MessageSenderBase<object>>();
senders.Add(new MessageSender1<MessageType1>());
where MessageSender1<MessageType1>
is derived from MessageSenderBase<T>
and MessageType1
is another class I defined.
Now the 2nd line has error which says CS1503 Argument 1: cannot convert from 'Demo.MessageSender.MessageSender1<Demo.MessageSender.MessageType1>' to 'Demo.MessageSender.MessageSenderBase<object>'
How can I solve this issue?
My design is that MessageSenderBase<T>
will sit in my library, and at the API level the user can derive their own sender class and specify their own type of message they want to send. I thought this would work since object
is the base class of MessageType1
and MessageSenderBase<>
is the base class of MessageSender1<>
.
Please help, thanks!
Edit - adding MessageSenderBase
public class MessageSender1<MessageType1> : MessageSenderBase<MessageType1>
{
public override string Topic => "topic1";
public async override Task SendAsync()
{
//...
}
}
public abstract class MessageSenderBase<T>
{
public abstract string Topic { get;}
public T Deserialize(string json)
{
return JsonConvert.DeserializeObject<T>(json);
}
public abstract Task SendAsync();
}
CodePudding user response:
One way to work around this is to add a new covariant interface that has the "shape" of MessageSenderBase<T>
:
public interface IMessageSender<out T> {
string Topic { get; }
T Deserialize(string json);
Task SendAsync();
}
public abstract class MessageSenderBase<T>: IMessageSender<T>
{
public abstract string Topic { get;}
public T Deserialize(string json)
{
return JsonConvert.DeserializeObject<T>(json);
}
public abstract Task SendAsync();
}
If you now make your list to be of type List<IMessageSender<object>>
, you can put AnyMessageSender<AnythingReferenceType>
in it.
var senders = new List<IMessageSender<object>>();
senders.Add(new MessageSender1<MessageType1>());
Note that if MessageSenderBase
has a method that takes a T
as parameter (or more generally, has a T
in an "input position"), you should not include that in IMessageSender
, because if you did, it is not safe to convert from MessageSender1<MessageType1>
to IMessageSender<object>
anymore. Imagine if you had:
public interface IMessageSender<out T> {
...
void Foo(T someT); // suppose this is implemented in MessageSender1
}
And you would be able to do:
var senders = new List<IMessageSender<object>>();
senders.Add(new MessageSender1<MessageType1>());
senders[0].Foo("abc"); // let's try giving a string to Foo
But senders[0]
is a MessageSender1<MessageType1>
object. MessageSender1<MessageType1>.Foo
only accepts MessageType1
objects, not string!