Простой способ обновления IEnumerable объектов с помощью LINQ

Предположим, что у меня есть такой бизнес-объект,

class Employee
    {
        public string name;
        public int id;
        public string desgination;
        public int grade;
    }

    List<Employee> lstEmp = new List<Employee>()
        {
            new Employee() { name="A",desgination="SE",id=1},
            new Employee() { name="b",desgination="TL",id=2},
            new Employee() { name="c",desgination="PL",id=3},
            new Employee() { name="d",desgination="SE",id=4},
            new Employee() { name="e",desgination="SSE",id=5},
        };

И если я хочу обновить класс сотрудника до 3, чье обозначение "SE", тогда я должен написать что-то вроде этого

lstEmp=lstEmp.Select(x =>
            {
                x.grade = (x.desgination == "SE") ? 3 : x.grade;
                    return x;
            }).ToList();

Но при использовании select он будет генерировать новый объект employee каждый раз, не обновляя существующий lstEmp, поэтому мне нужно повторно обновить список до lstEmp.

Мне кажется, это влияет на производительность при частом обновлении больших обновлений. Есть ли обходной путь для этого?

Ответ 1

Я считаю, что методы LINQ по умолчанию поддерживают встроенные обновления. но вы можете создать свои собственные методы расширения, чтобы достичь этого.

  public static void Update<TSource>(this IEnumerable<TSource> outer, Action<TSource> updator)
        {
            foreach (var item in outer)
            {
                updator(item);
            }


        }

и используйте его так:

 lstEmp.Update(x => x.grade = (x.desgination == "SE") ? 3 : x.grade);

Ответ 2

Собственно, существующий вызов Select изменяет сами исходные объекты - это не создает новые объекты-сотрудники. Что заставляет вас думать, что он создает новые экземпляры Employee? В конце концов, вы не получили new Employee в любом месте выражения лямбда.

Вы можете повторять результаты вызова Select без вызова ToList, и вы все равно увидите изменения после этого. Это означает, что у вашей проекции есть побочные эффекты - это, как правило, плохая идея. Однако вы должны сделать что-то итеративно по результатам проекции. Например, просто вызов Count() сделает это. Запросы LINQ во многих случаях используют отложенное выполнение: они не применяют проектор/предикат/etc до тех пор, пока результат не понадобится.

LINQ предназначен для работы в функциональном режиме: побочные эффекты не рекомендуется, так же, как вы не ожидаете, что обычный запрос SELECT в базе данных изменит содержимое таблицы, которую он запрашивает. Бездефектный код отлично подходит с точки зрения удобочитаемости и способности рассуждать о коде.

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

Ответ 3

Я бы сказал, что LINQ является основным для выбора данных после получения данных, вы можете использовать конструкцию ForEach для обновления данных

Ответ 4

lstEmp = lstEmp.Select(X => new Employee
        {
            desgination =X.desgination ,
            grade =X.desgination=="SE"?3:X.grade ,
            id=X.id ,
            name =X.name 
        }).ToList();