如何在LINQ中将匿名类型转换为强类型?

时间:2009-04-29 12:58:34

标签: c# linq .net-3.5

我有一个ListViewItems数组(ListViewItem[]),我在每个ListViewItem.Tag中存储一个SalesOrderMaster对象供以后参考。

我现在有一些代码,通过每个ListViewItem安全地将.Tag属性转换为SalesOrderMaster对象,然后将该对象添加到SalesOrders的集合中,只有在检查后才能确保订单没有t已存在于该集合中。

比较销售订单的流程非常昂贵,我想将其转换为LINQ表达式,以提高清晰度和性能。 (我也安装了Parallel Extensions to .NET Framework 3.5所以我可以用它来进一步提高LINQ性能)

所以不用多说:这就是我拥有的,然后是我想要的。 (我想要的东西不会编译,所以我知道我做错了什么,但我希望它说明了这一点)

我拥有的:(慢)

foreach (ListViewItem item in e.Argument as ListViewItem[])
            {
                SalesOrderMaster order = item.Tag as SalesOrderMaster;
                if ( order == null )
                {
                    return;
                }
                if (!All_SalesOrders.Contains(order))
                {
                    All_SalesOrders.Add(order);
                }
            }

我想要的:(理论)

    List<SalesOrderMaster> orders = 
(from item in (e.Argument as ListViewItem[]).AsParallel() 
select new { ((SalesOrderMaster)item.Tag) }).Distinct();
编辑:我知道演员阵容便宜,我说“比较”,在这种情况下转换为.Contains(订单)操作

编辑:每个人的回答都很棒!我希望我可以标记多个答案,但最后我必须选择一个。

编辑:这就是我最终的结果:

List<SalesOrderMaster> orders = 
(from item in (e.Argument as ListViewItem[]) select (SalesOrderMaster) item.Tag).GroupBy(item => item.Number).Select(x => x.First()).ToList();

3 个答案:

答案 0 :(得分:18)

我看到没有人明确表示你需要将匿名类型转换为命名类型,所以这里...通过使用“select new { }”你创建了一个匿名类型,但你不需要。您可以这样编写查询:

List<SalesOrderMaster> orders = 
    (from item in (e.Argument as ListViewItem[]).AsParallel() 
    select (SalesOrderMaster)item.Tag)
    .Distinct()
    .ToList();

请注意,查询选择(SalesOrderMaster)item.Tag而不是new { },因此它不会创建匿名类型。另请注意,我添加了ToList(),因为您需要List<SalesOrderMaster>

这解决了您的匿名类型问题。但是,我同意Mark和Guffa的说法,在这里使用并行查询不是最佳选择。要使用HashSet<SalesOrderMaster>作为Guffa建议,您可以这样做:

IEnumerable<SalesOrderMaster> query = 
    from item in (ListViewItem[])e.Argument
    select (SalesOrderMaster)item.Tag;

HashSet<SalesOrderMaster> orders = new HashSet<SalesOrderMaster>(query);

(我避免使用var,因此示例中的返回类型是明确的。)

答案 1 :(得分:3)

该代码中昂贵的部分是调用列表中的Contains方法。因为它是一个O(n)操作,所以你添加到列表中的对象越多,它就越慢。

只需使用HashSet<SalesOrderMaster>代替List<SalesOrderMaster>Contains的{​​{1}}方法是O(1)操作,因此您的循环将是O(n)操作而不是O(n * n)操作。

答案 2 :(得分:3)

就像Marc Gravell所说,你不应该从不同的线程访问Tag属性,并且演员阵容非常便宜,所以你有:

var items = (e.Argument as ListViewItem[]).Select(x=>x.Tag)
         .OfType<SalesOrderMaster>().ToList();

但是,您希望找到不同的项目 - 您可以尝试使用AsParallel

var orders = items.AsParallel().Distinct();