Как преобразовать анонимный тип в сильный тип в LINQ?
У меня есть массив ListViewItems (ListViewItem[]
), где я храню объект SalesOrderMaster
в каждом элементе ListViewItem.Tag для последующей ссылки.
У меня есть некоторый код, который прямо сейчас, через каждый ListViewItem
безопасно передает свойство .Tag в объект SalesOrderMaster, а затем добавляет этот объект в коллекцию SalesOrders, только после проверки, чтобы убедиться, что заказ еще не установлен существуют в этой коллекции.
Процесс сравнивать заказы на продажу является дорогостоящим, и я хотел бы преобразовать его в выражение LINQ для ясности и производительности. (У меня также есть Parallel Extensions to.NET Framework 3.5, поэтому я могу использовать его для дальнейшего улучшения производительности LINQ)
Итак, без дальнейших церемоний: Это то, что у меня есть, а затем то, что я хочу. (то, что я хочу, не будет компилироваться, поэтому я знаю, что я делаю что-то неправильно, но надеюсь, что это иллюстрирует суть)
Что у меня: (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);
}
}
Что я хочу: (Теория)
List<SalesOrderMaster> orders =
(from item in (e.Argument as ListViewItem[]).AsParallel()
select new { ((SalesOrderMaster)item.Tag) }).Distinct();
EDIT: Я знаю, что акция дешевая, я сказал "Сравнить", которая в этом случае переводит на операцию .Contains(order)
EDIT: Каждый ответ был потрясающим! Хотел бы я отметить более одного ответа, но в конце концов я должен выбрать один.
EDIT: Это то, с чем я закончил:
List<SalesOrderMaster> orders =
(from item in (e.Argument as ListViewItem[]) select (SalesOrderMaster) item.Tag).GroupBy(item => item.Number).Select(x => x.First()).ToList();
Ответы
Ответ 1
Я вижу, что никто не обращался к вашей потребности в явном преобразовании анонимного типа в именованный тип, поэтому здесь идет... Используя "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>
.
Это решает проблему анонимного типа. Тем не менее, я согласен с Марком и Гуффа в том, что использование параллельного запроса здесь не лучший вариант. Чтобы использовать HashSet<SalesOrderMaster>
, как предположил Гуффа, вы можете сделать это:
IEnumerable<SalesOrderMaster> query =
from item in (ListViewItem[])e.Argument
select (SalesOrderMaster)item.Tag;
HashSet<SalesOrderMaster> orders = new HashSet<SalesOrderMaster>(query);
(Я избегал использования var
, поэтому возвращаемые типы ясны в примерах.)
Ответ 2
Часть дорогостоящего кода вызывает вызов метода Contains
в списке. Поскольку это операция O (n), она становится медленнее, чем больше объектов, которые вы добавляете в список.
Просто используйте HashSet<SalesOrderMaster>
для объектов вместо List<SalesOrderMaster>
. Метод Contains
для HashSet
является операцией O (1), поэтому ваш цикл будет выполнять операцию O (n) вместо операции O (n * n).
Ответ 3
Как сказал Марк Гравелл, вы не должны обращаться к свойству Tag
из разных потоков, а приведение довольно дешево, поэтому у вас есть:
var items = (e.Argument as ListViewItem[]).Select(x=>x.Tag)
.OfType<SalesOrderMaster>().ToList();
но затем вы хотите найти отдельные элементы - здесь вы можете попробовать использовать AsParallel
:
var orders = items.AsParallel().Distinct();