【问题标题】:How to specify generic types at runtime?如何在运行时指定泛型类型?
【发布时间】:2023-03-14 10:20:02
【问题描述】:

我有一段这样的代码:

  var senders = new List<MessageSenderBase<object>>();
  senders.Add(new MessageSender1<MessageType1>());

其中MessageSender1&lt;MessageType1&gt; 派生自MessageSenderBase&lt;T&gt;MessageType1 是我定义的另一个类。

现在第二行有错误,上面写着CS1503 Argument 1: cannot convert from 'Demo.MessageSender.MessageSender1&lt;Demo.MessageSender.MessageType1&gt;' to 'Demo.MessageSender.MessageSenderBase&lt;object&gt;'

我该如何解决这个问题?

我的设计是MessageSenderBase&lt;T&gt; 将位于我的库中,并且在 API 级别,用户可以派生自己的发件人类并指定他们自己想要发送的消息类型。我认为这会起作用,因为objectMessageType1 的基类,而MessageSenderBase&lt;&gt;MessageSender1&lt;&gt; 的基类。

请帮忙,谢谢!

编辑 - 添加 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();
    }

【问题讨论】:

  • 你能展示一下你的MessageSenderBase类是什么样的吗?从MessageSender1&lt;MessageType1&gt;MessageSender1&lt;object&gt; 的转换可能有意义,也可能没有意义。
  • @Sweeper 请看我最近的编辑,谢谢!
  • 这是一个编译时静态类型错误。此时运行时无关紧要。
  • 如果MessageSenderBase&lt;T&gt;只有你展示的那个方法,你可以把它做成covariant接口,然后转换就可以了。
  • @Sweeper 谢谢,如果我的课堂上有一些实现,它会起作用吗?请参阅我最近的编辑。我无法将其标记为接口。

标签: c# .net generics polymorphism


【解决方案1】:

解决此问题的一种方法是添加一个新的covariant 接口,其“形状”为MessageSenderBase&lt;T&gt;

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

如果您现在将列表设为List&lt;IMessageSender&lt;object&gt;&gt; 类型,则可以将AnyMessageSender&lt;AnythingReferenceType&gt; 放入其中。

var senders = new List<IMessageSender<object>>();
senders.Add(new MessageSender1<MessageType1>());

请注意,如果MessageSenderBase 有一个将T 作为参数的方法(或更一般地说,在“输入位置”中有T),则不应将其包含在IMessageSender 中,因为如果你做到了,从MessageSender1&lt;MessageType1&gt; 转换为IMessageSender&lt;object&gt; 不再安全。想象一下,如果你有:

public interface IMessageSender<out T> {
    ...
    void Foo(T someT); // suppose this is implemented in MessageSender1
}

你可以这样做:

var senders = new List<IMessageSender<object>>();
senders.Add(new MessageSender1<MessageType1>());
senders[0].Foo("abc"); // let's try giving a string to Foo

但是senders[0] 是一个MessageSender1&lt;MessageType1&gt; 对象。 MessageSender1&lt;MessageType1&gt;.Foo 只接受 MessageType1 对象,不接受字符串!

【讨论】:

  • 非常感谢!我需要进一步了解协方差。我几乎拥有完全相同的东西,但只是缺少界面的“out”关键字。
  • 在计算协方差时,请继续阅读逆变(使用in而不是out
猜你喜欢
  • 2019-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多