Подтвердить что ты не робот

Как преобразовать анонимный тип в сильный тип в 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();
4b9b3361

Ответ 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();