【问题标题】:Implicit Conversion over a Collection对集合的隐式转换
【发布时间】:2011-06-08 22:11:59
【问题描述】:

本周我遇到了一个关于 C# 中对集合进行隐式转换的问题。虽然这(使用implicit)可能不是我们的最终方法,但我想至少完成代码以提供团队作为选项。我将问题归结为以下示例案例:

我的示例中有两个类:一个代表业务对象 (Foo),另一个代表此业务项 (FooVO) 的客户端版本(视图对象),定义如下...

public class Foo
{
    public string Id {get; set;}

    public string BusinessInfo {get; set;}
}

public class FooVO
{
    public string Id {get; set;}

    public static implicit operator FooVO( Foo foo )
    {
        return new FooVO { Id = foo.Id };
    }
}

我的问题是当我有一个 Foo 对象列表并想使用我的隐式运算符将它们转换为 FooVO 对象列表时。

List<Foo> foos = GetListOfBusinessFoos(); // Get business objects to convert

我试过了

List<FooVO> fooVOs = foos; // ERROR

List<FooVO> fooVOs = (List<FooVO>) foos; // ERROR

甚至

List<FooVO> fooVOs = foos.Select( x => x ); // ERROR

我知道我可以循环执行此操作,但我希望使用直接(LINQ?)的方式一次性转换对象。有什么想法吗?

提前谢谢你。

编辑 修正了示例中的错字

【问题讨论】:

  • 您在该示例中对FooVO 的隐式转换实际上返回了Foo,这是故意的吗?
  • @Chris Walsh - 不,这是一个错字。我会纠正它。谢谢你抓住它。

标签: c# linq implicit-conversion


【解决方案1】:

几乎每天都会在 SO 上提出类似的问题。你不能这样做,因为这样做违反了类型安全:

List<Giraffe> g = new List<Giraffe>();
List<Animal> a = g; // Should this be legal?
a.Add(new Tiger()); // Nope; we just added a tiger to a list of giraffes.

在 C# 4.0 中,您可以从 IEnumerable&lt;Giraffe&gt; 隐式转换为 IEnumerable&lt;Animal&gt;,因为没有“添加”方法可以解决问题。但是如果元素类型的转换是用户定义的,那么你永远不能进行这样的“协变”转换。它必须是 referenceidentity 转换。

您需要创建第二个列表并一次复制一个。或者使用 Select 和 ToList 等 LINQ 辅助方法为您完成这项工作。

你想要的类型系统概念的名称是“协方差”;协变关系是您推理“长颈鹿是动物,因此长颈鹿的序列是动物的序列”的关系。如果您对此主题感兴趣,那么您可以在此处阅读我们如何在 C# 4.0 中添加协变(和逆变):

http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/default.aspx

从底部开始。

【讨论】:

  • 很好的例子,埃里克。谢谢。
  • OP 从未说过 FooVO 是 Foo 的子类,因此 a.Add(new Tiger()) 不会编译。 OP 询问了需要相互转换的 2 个单独的类。这种情况在 MVVM 编程中经常出现。我从数据库中获取 List 并需要转换为 List 以将数据绑定到视图。
  • // 不;我们刚刚在长颈鹿列表中添加了一只老虎。不,您将老虎添加到动物列表中,这很好。我不明白为什么这会破坏类型安全。
  • @LHolleman:引用类型的值是references。现在清楚了吗?
  • 从返回类型为 List 的函数返回 List 时不起作用
【解决方案2】:

您的示例不起作用的原因是您试图将IEnumerable&lt;FooVO&gt; 分配给List&lt;FooVO&gt;。以下应该可以工作。

 List<FooVO> fooVos = foos.Select<Foo,FooVO>(x => x).ToList();

【讨论】:

    【解决方案3】:
    List<FooVO> d = new List<FooVO>(foos.Select(x => (FooVO)x)); 
    

    为我工作。

    【讨论】:

    • 啊哈 - 在 Select lambda 中进行投射。这也适用于我。谢谢。
    • 这是让演员从 .ToListAsync() 工作的唯一方法。谢谢大佬!
    【解决方案4】:

    使用 ConvertAll 方法传入 static implicit operator FooVO( Foo foo ) 作为参数

    List<FooVO> fooVOs = foos.ConvertAll<FooVO>(FooVO.FooVO);
    

    【讨论】:

    • 我的上述示例代码和您的建议出现编译器错误。但是,如果我将 (FooVO.FooVO) 替换为 (x => x) 它将起作用。我之前没有使用过 ConvertAll() - 谢谢你的建议。
    猜你喜欢
    • 1970-01-01
    • 2011-06-08
    • 1970-01-01
    • 2011-06-20
    • 2014-08-28
    • 2015-10-28
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    相关资源
    最近更新 更多