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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/802256/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 01:47:03  来源:igfitidea点击:

How can I convert anonymous type to strong type in LINQ?

c#linq.net-3.5

提问by Russ

I have an array of ListViewItems ( ListViewItem[]), where I store a SalesOrderMasterobject in each ListViewItem.Tag for later reference.

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

I have some code that right now, goes through each ListViewItemsafely casts the .Tag property into a SalesOrderMaster object, then adds that object to a collection of SalesOrders, only after checking to make sure the order doesn't already exist in that collection.

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

The process to comparesales orders is expensive, and I would like to convert this to a LINQ expression for clarity and performance. ( I also have the Parallel Extensions to .NET Framework 3.5installed so I can use that to further improve LINQ performance)

比较销售订单的过程很昂贵,为了清晰和性能,我想将其转换为 LINQ 表达式。(我还安装了.NET Framework 3.5并行扩展,因此我可以使用它来进一步提高 LINQ 性能)

So without further ado: This is what I have, and then what I want. ( what I want won't compile, so I know I am doing something wrong, but I hope it illustrates the point )

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

What I have: ( Slow )

我有什么:(慢)

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

What I want: ( Theory )

我想要的:(理论)

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

EDIT: I know the cast is cheap, I said the "Compare", which in this case translates to the .Contains(order) operation

编辑:我知道演员表很便宜,我说的是“比较”,在这种情况下转换为 .Contains(order) 操作

EDIT: Everyone's answer was awesome! I wish I could mark more than one answer, but in the end I have to pick one.

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

EDIT : This is what I ended up with:

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

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

采纳答案by Lucas

I see nobody has addressed your need to convert an anonymous type to a named type explicitly, so here goes... By using "select new { }" you are creating an anonymous type, but you don't need to. You can write your query like this:

我看到没有人解决了您将匿名类型显式转换为命名类型的需求,所以这里是......通过使用“ select new { }”,您正在创建一个匿名类型,但您不需要。您可以像这样编写查询:

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

Notice that the query selects (SalesOrderMaster)item.Tagwithout new { }, so it doesn't create an anonymous type. Also note I added ToList()since you want a List<SalesOrderMaster>.

请注意,查询选择(SalesOrderMaster)item.Tagwithout new { },因此它不会创建匿名类型。还要注意我添加的ToList()因为你想要一个List<SalesOrderMaster>.

This solves your anonymous type problem. However, I agree with Mark and Guffa that using a parallel query here isn't you best option. To use HashSet<SalesOrderMaster>as Guffa suggested, you can do this:

这解决了您的匿名类型问题。但是,我同意 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);

(I avoided using varso the returned types are clear in the examples.)

(我避免使用,var因此示例中返回的类型很清楚。)

回答by Guffa

The part in that code that is expensive is calling the Containsmethod on the list. As it's an O(n) operation it gets slower the more objects you add to the list.

该代码中开销较大的部分是调用Contains列表中的方法。由于它是一个 O(n) 操作,因此添加到列表中的对象越多,它就越慢。

Just use a HashSet<SalesOrderMaster>for the objects instead of a List<SalesOrderMaster>. The Containsmethod of the HashSetis an O(1) operation, so your loop will be an O(n) operation instead of an O(n*n) operation.

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

回答by configurator

Like Marc Gravell said, you shouldn't access the Tagproperty from different threads, and the cast is quite cheap, so you have:

就像 Marc Gravell 所说的那样,您不应该Tag从不同的线程访问该属性,并且演员表非常便宜,因此您有:

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

but then, you want to find distinct items - here you can try using AsParallel:

但是,您想找到不同的项目 - 在这里您可以尝试使用AsParallel

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