Как применить C # LINQ для устранения циклов foreach?

#c# #performance #linq

#c# #Производительность #linq

Вопрос:

Как реструктурировать эти структуры данных и код обработки данных, чтобы использовать LINQ для краткости, ясности и эффективности использования ресурсов (процессор память)?

 public class Account
{
    public string AccountNumber {get; set;}  
}

public class Customer
{
    public List<Account> Accounts {get; set;}  
}

public static void TransformAccount(Account account)
{
    if (account != null amp;amp; account.AccountNumber != null)
    {
        account.AccountNumber = account.AccountNumber.Trim().TrimStart(new char[] {'0'});
    }
}

public static void TransformCustomer(Customer customer)
{
    if (customer == null || customer.Accounts == null) return;
    foreach (var account in customer.Accounts)
    {
        TransformAccount(account);
    }
}

public static ProcessCustomers(List<Customer> customers)
{
    if (customers != null)
    {
      foreach (var customer in customers)
      {
          TransformCustomer(customer);
      }
    }
}
  

Комментарии:

1. К вашему сведению: Хотя использование Linq может помочь с удобочитаемостью кода и ремонтопригодностью, оно редко заставляет код выполняться быстрее или эффективнее по сравнению с эквивалентным циклом for / foreach / while.

2. И хотя LINQ отлично подходит для запросов к данным, он не предназначен для использования для изменения данных, как вы делаете здесь. foreach циклы обычно считаются правильным способом сделать это. По этой причине LINQ не предоставляет ForEach метод расширения.

3. Я не вижу никаких улучшений, которые может внести linq, вы могли бы сделать что-то вроде: customers .ForEach(x => { if x!= null) TransformCustomer(customer);}); но на самом деле это то же самое, только foreach (я думаю) должен быть еще медленнее.

4. and resource efficiency (cpu memory ) — как вы думаете, что именно делает LINQ? Вот код для LINQ .Where(...) .

5. morelinq.github.io/2.8/ref/api/html/…

Ответ №1:

LINQ лучше подходит для запросов, чем для изменения данных. Тем не менее, вы могли бы использовать LINQ, чтобы получить элементы учетной записи, которые вы хотите изменить, и внести изменения в foreach.

 var customers = GetCustomers(); // Some method that gets a customer list

var toModify = from customer in customers where customer.Accounts != null
               from account in customer.Accounts where account.AccountNumber != null
               select account;

foreach(var item in toModify)
    item.AccountNumber = item.AccountNumber.Trim().TrimStart(new char[]{'0'});
  

Это не обязательно будет более производительным, чем просто использование циклов foreach, но вы можете счесть это более читабельным. Итак, хотя вы могли бы добиться краткости и ясности, я не думаю, что вы также получите производительность (как указано в комментариях).

Ответ №2:

Вот способ сделать это в LINQ.

При использовании фиктивного списка из 1000 клиентов с 1000 учетными записями в каждом ваш код с foreach занял 75 миллисекунд, а этот LINQ занял 123 миллисекунды, почти в два раза больше времени.

     public static void ProcessCustomersLinq(List<Customer> customers)
    {
        customers?
            .Where(c => c != null amp;amp; c.Accounts != null)
            .SelectMany(c => c.Accounts.Where(a => a != null))
            .ToList()
            .ForEach(a => TrimAccountNumber(a));
    }

    private static void TrimAccountNumber(Account account)
    {
        account.AccountNumber = account.AccountNumber.Trim().TrimStart(new char[] { '0' });
    }
  

Комментарии:

1. Рассмотрите возможность использования morelinq.github.io/2.8/ref/api/html / … чтобы избежать затрат на выделение памяти для ToList .