【问题标题】:Cast an object with a generic type without knowing the type在不知道类型的情况下使用泛型类型转换对象
【发布时间】:2021-03-31 12:46:33
【问题描述】:

根据以下对象定义:

public interface IMessage
{
    Guid Identifier { get; set; }
}

public class Message : IMessage
{
    public Guid Identifier { get; set; }
}

public interface IEventMessage<TPayload> : IMessage
{
    TPayload Payload { get; set; }
}

public class EventMessage<TPayload> : Message, IEventMessage<TPayload>
{
    public TPayload Payload { get; set; }
}

public interface ICommandMessage<TParameters> : IMessage
{
    TParameters Parameters { get; set; }
}

public class CommandMessage<TParameters> : Message, ICommandMessage<TParameters>
{
    public TPayload Payload { get; set; }
}

我想根据消息的类型过滤消息集合,而不考虑它们的通用类型

var messages = new List<IMessage>
{
    new CommandMessage<string>() { Name = "Command Message String" },
    new CommandMessage<int>() { Name = "Command Message Int" },
    new EventMessage<string>() { Name = "Event Message String" },
    new EventMessage<int>() { Name = "Event Message Int" }
};

var onlyStringCommandMessages = messages.OfType<CommandMessage<string>>();  // OK
var onlyIntCommandMessages = messages.OfType<CommandMessage<int>>();        // OK
var onlyCommandMessages = messages.OfType<CommandMessage<object>>();        // KO
var onlyCommandMessages2 = messages.OfType<CommandMessage<dynamic>>();      // KO

我尝试过其他技术,例如:

var onlyCommandMessages3 = messages.Where(x => 
  x.GetType()
  .GetInterfaces()
  .Any(i => i.IsGenericType && (i.GetGenericTypeDefinition() == typeof(ICommandMessage<>)))); // OK but return IMessage types

它似乎工作,但它返回我 IMessage 类型。如何同时投射到 ICommandMessage?

在 Typescript 中使用 any 类型会非常有用。 ICommandMessage&lt;any&gt;

我找到了another subject related to this problem,但这对我没有帮助。

有什么想法吗?

【问题讨论】:

  • 创建一个非泛型版本的IEventMessageICommandMessage 来继承泛型版本怎么样?
  • 在某些时候,这就是我所做的。但是在更高的级别(库),我需要处理不同类型的消息,而不管它们的通用类型。唯一重要的是它们的“主要”类型(事件、命令……)
  • 查询的结果应该是什么?如果您将问题转换为List&lt;T&gt;,则不能说List&lt;&gt; anyList = yourQuery.ToList()。您需要泛型类型参数。 List&lt;string&gt;List&lt;int&gt; 不同。您的ICommandMessage&lt;T&gt; 也是如此。
  • 虽然您可以过滤这些泛型类型,但结果只能转换为 object。如果您需要访问 非泛型部分,则必须创建一个额外的非泛型接口并对其进行过滤。如果这不是有意或不可能的,那么您必须编写一些反射代码或使用dynamic(对于整个类型,而不仅仅是泛型参数)。
  • 您正在处理一个不太适合您需求的类结构。显然,您需要一种逻辑方式来访问所有 CommandMessage 对象(不管任何泛型参数),但您的代码一开始就不包含这种类型。这只是意味着您需要重新评估您的设计,以便它可以包含这样的类型。这种类型的外观很大程度上取决于您打算用它做什么。那么,在你获取了CommandMessage 对象之后,你想用它们做什么呢?有什么涉及他们的通用属性吗?还是其他不依赖它的东西?

标签: c# linq generics


【解决方案1】:

不完全是您所要求的,但是(受IEnumeratorEnumerator&lt;T&gt; 的启发)您使用非通用接口,例如:

public interface ICommandMessage : IMessage
{
    object Parameters { get; set; }
}

public interface ICommandMessage<TParameters> : ICommandMessage
{
    TParameters Parameters { get; set; }
}

public class CommandMessage<TParameters> : Message, ICommandMessage<TParameters>
{
    public TParameters Parameters { get; set; }
    object ICommandMessage.Parameters
    {
        get => Parameters;
        set => Parameters = (TParameters)value;
    }
}

那么你可以:

var onlyCommandMessages = messages.OfType<ICommandMessage>();

从@Flater 评论编辑:

使用这种结构,您无法保证实现ICommandMessage&lt;TParameters&gt; 的类将向ICommandMessage&lt;TParameters&gt;.ParameterICommandMessage.Parameter 返回相同的对象。 IEnumerator&lt;T&gt; 也是同样的问题。

就像@Flater 建议的那样,您可以最小化ICommandMessage 以满足您的需要。

【讨论】:

  • 假设 OP 对访问参数本身不感兴趣(假设他们不关心这些参数的类型),最好将 ICommandMessage 设为空标记界面。它允许相同的区别,没有肮脏且容易出错的object Parameters 属性。
  • 你是对的。创建一个没有泛型参数的 ICommandMessage 接口就可以了。即使我真的不喜欢空的界面。无论如何,我还有其他问题......我的结论是我的设计有问题,我需要改变它。谢谢大家
猜你喜欢
  • 1970-01-01
  • 2017-03-11
  • 1970-01-01
  • 2012-09-19
  • 1970-01-01
  • 2013-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多