【问题标题】:Casting object to List<(Enum, string)> issue将对象转换为 List<(Enum, string)> 问题
【发布时间】:2021-05-15 09:34:33
【问题描述】:

我正在通过实现IMultiValueConverter with *Convert(object[] values, Type targetType, object parameter, CultureInfo culture)来创建转换器

我正在传递一个List&lt;(SomeEnumType, string)&gt; 元组。

通过 MultiBinding 并在转换器端我想进行转换,但它会引发转换错误。

我试过了: var result = (List&lt;(Enum, string)&gt;)values[1];

但我遇到了这个选角问题:

'无法将'System.Collections.Generic.List1[System.ValueTuple2[Vasco.Basics.Contracts.CoreConfigurations.Enums.ApplicationType,System.String]]'类型的对象转换为'System.Collections.Generic。 List1[System.ValueTuple2[System.Enum,System.String]]'.'

这很奇怪,因为如果我只传递 SomeEnumType 的一个元素并尝试像(Enum)values[1] 这样的大小写效果很好。

当我通过List&lt;SomeEnumType&gt; 并尝试像(List&lt;Enum&gt;)values[1] 一样投射时已经不起作用了。

提前谢谢你!

【问题讨论】:

  • 如果它是一个元组列表,为什么不这样声明:List&lt;Tuple&lt;SomeEnumType, string&gt;&gt; tuple
  • 您能否发布更多代码,以便我们了解可能发生的情况,好吗?要转换为列表,最好使用 Linq 扩展方法,例如values[1].ToList().
  • @jonathana 在最新版本的 C# (7+) 中这是声明元组的方式 - docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
  • @bcg 不知道,感谢更新。
  • 就像 jonathana 说的你应该像 List> 一样声明元组

标签: c# list casting tuples


【解决方案1】:

当我传递一个列表并尝试像 (List)values1 一样进行投射时,它已经不起作用了。

一般不得将通用集合List&lt;T&gt;IEnumerable&lt;T&gt; 转换为其他类型。这归结为 C# 和编译器如何处理泛型以及称为 Covariance and contravariance 的东西。这是一个令人难以置信的复杂话题,至少对我来说是这样,所以我不会用细节来困扰你。

考虑以下情况。

List<string> strings = new() { "Kitten", "Mouse", "horse" };
List<object> objs = strings;

这看起来很自然,尤其是如果您尝试显式转换 strings 列表,例如 (List&lt;object&gt;)strings,但这不会编译,这是一件好事!它可以保护您免于做傻事,例如:

List<string> strings = new() { "Kitten", "Mouse", "horse" };
List<object> objs = strings;
objs.Add(1.29d);

这似乎只是与您的问题无关,但这非常重要,这也是您不能将集合转换为不同类型集合的确切原因,即使您知道它们非常相似。

当我们将 double 添加到 objs 列表时(假设这会编译,它不会编译),有效的做法是将 double 添加到 List&lt;string&gt; 这将破坏关于如何强类型语言(例如 C#)可以工作。

这很奇怪,因为如果我只传递 SomeEnumType 的一个元素并尝试像 (Enum)values1 一样大小写,效果很好。

您可以这样做而不是集合的原因是因为编译器可以检查单个对象以查看是否存在有效的转换并手动为您进行转换。与编译器的集合不同,如果它与单个对象执行相同的操作,它会将某些内容添加到集合中,这些内容可能与该集合初始化时所限制的类型不匹配。

感谢John Skeet 的解释,Ch4.4.1 ISBN 9781617294532

【讨论】:

  • 很好的解释。但请注意,“不允许将泛型集合转换为其他类型” 通常是不正确的,因为数组是此规则的例外(因为您无法修改它们)。 stackoverflow.com/a/17619183/284240
  • 是的,你完全正确!我试图在开头强调一般部分,因为使用 c# 总是有奇怪的例外!
【解决方案2】:

一般来说,您不能像这样转换列表 - 因为您实际上要做的是转换列表中的每个item,而不是列表本身。因此,您需要逐个循环并投射每个项目,如下所示:

var input = new List<(SomeEnumType, string)>();
// now add items to the input list

var result = new List<(Enum, string)>();
foreach (var element in input)
{
    result.Add(
        ((Enum)element.Item1, element.Item2)
    );
}

请记住,元组不是单个元素,而是多个元素的包装器,每个元素都需要强制转换。

或者,您可以使用允许您“映射”类型的工具,例如MapsterAutoMapper - 我个人更喜欢 Mapster。

using Mapster;

var input = new List<(SomeEnumType, string)>();
// now add items to the input list

var result = input.Adapt<List<(Enum, string)>>();
// Adapt<>() is an extension method provided by Mapster

【讨论】:

  • 我尝试使用 foreach 并强制转换每个元素只有在源为 List&lt;SomeEnumType&gt;(); 时才能正常工作,当我使用 List&lt;(SomeEnumType, string)&gt;(); 作为源时,则无法转换元素。
  • 那是因为元组实际上是两个元素合二为一,因此您还必须在元组中强制转换每个元素 - 我将更新我的答案以反映这一点。我建议探索映射工具,因为它们会做你想做的事。
猜你喜欢
  • 1970-01-01
  • 2017-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-17
  • 1970-01-01
相关资源
最近更新 更多