C#: обновить значение элемента в списке



Я ищу способ обновить элемент в списке, не перечисляя его самостоятельно.



Я получил класс MyProjects, который содержит список именованных проектов.



Я хочу найти MyProjects.Проекты-класс, где свойство члена Class1 (имя) равно значению "накладные расходы".



Что работает:



foreach (Project prj in MyProjects.Projects) {
if (prj.Name == "Overhead")
prj.IsActive = true;
};


Я, однако, попытался сделать то же самое с помощью Linq, но не смог написать его в виде одной строки. Возможно ли это вообще? Причина, по которой я не люблю повторять в пути выше разве что я уже перебираю весь список в этом кодблоке и думаю, что мог бы быть более красивый способ :)

1193   3  

3 ответов:

Вы не должны пытаться свести все к одной строке - настолько короткой, насколько это возможно для чтения. В этом случае можно использовать:

foreach (var project in MyProjects.Projects.Where(p => p.Name == "Overhead"))
{
    project.IsActive = true;
}

Это использование LINQ для части запроса , которая подходит, поскольку это то, что означает Q LINQ. Я настоятельно призываю вас не мутировать элементы внутри вызовов LINQ так, как это делает ответ Mayank. Он подвержен ошибкам (о чем свидетельствует оригинальный ответ, не работающий) и противоречит духу LINQ.

Это примерно так читабельно, как получится, ИМО. Он делает точно то же самое, что и исходный код, заметьте - вы не можете избежать чего-то, повторяющего каждый элемент в списке, если каждый элемент может быть тем, что вы хотите обновить.

EDIT: просто для смеха, если Вы действительно, действительно хотели сделать это в довольно минимальном коде, вы могли бы использовать:

// DON'T USE THIS!
MyProjects.Project.Count(p => p.Name == "Overhead" && (p.IsActive = true));

Здесь мы используем тот факт, что && является коротким замыканием, чтобы избежать оценки назначения (p.IsActive = true), Если условие не соответствует. Удобно, что мы присваиваем свойству значение bool, поскольку это означает, что нам не нужно делать ничего другого, чтобы сделать его допустимым вторым операндом для оператора &&. Мы используем Count(), чтобы полностью оценить результат без создания каких - либо дополнительных списков и т. д.-И мы используем версию с предикатом, чтобы избежать даже необходимости вызова Where, что делала предыдущая версия. (LastOrDefault тоже сработает.) Но это все ужасное злоупотребление, и никогда не должно появляться в каком-либо реальном коде.

Я придумал способ свести его к одной строке, не злоупотребляя LINQ, так как я использую его только для части запроса (фильтра) и использую пользовательский метод расширения для выполнения действия настройки свойства. Вы все еще собираетесь перечислять элементы (вы должны), но вы можете скрыть это в методе расширения. Я подозреваю, что вам на самом деле было все равно, перечисляете вы элемент или нет, вам просто не нравилось количество видимого пространства, которое цикл foreach будет занимать в вашем основном код.

Используйте этот метод расширения:

public static IEnumerable<T> SetProperty<T>(this IEnumerable<T> list, Action<T> action)
{
    foreach (var item in list)
    {
        action.Invoke(item);
    }
    return list;
}

Это позволяет свести его к одной читаемой строке.

Projects.Where(p => p.Name == "Overhead").SetProperty(p => p.IsActive = true);

Полная тестовая программа:

using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            var Projects = new List<Project>() {
                new Project() { Name="Overhead", IsActive=false },
                new Project() { Name="Nadfadfs", IsActive=false },
                new Project() { Name="Overhead", IsActive=false },
                new Project() { Name="dasfasdf", IsActive=false }
            };
            PrintProjectList(Projects);
            Console.WriteLine("--Setting property--");
            Projects.Where(p => p.Name == "Overhead").SetProperty(p => p.IsActive = true);
            PrintProjectList(Projects);
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }

        static void PrintProjectList(IEnumerable<Project> projects)
        {
            foreach(var p in projects)
            {
                Console.WriteLine($"Name: {p.Name} IsActive: {p.IsActive}");
            }
        }
    }

    class Project
    {
        public string Name { get; set; }
        public bool IsActive { get; set; }
    }

    public static class Extensions
    {
        public static IEnumerable<T> SetProperty<T>(this IEnumerable<T> list, Action<T> action)
        {
            foreach (var item in list)
            {
                action.Invoke(item);
            }
            return list;
        }
    }
}

Вывод:

Имя: Накладные Расходы IsActive: False

Имя: Nadfadfs IsActive: False

Имя: Накладные Расходы IsActive: False

Имя: dasfasdf IsActive: False

--Свойство Установки--

Имя: Накладные Расходы IsActive: True

Имя: Накладные Расходы IsActive: False

Имя: Накладные Расходы IsActive: True

Имя: Накладные Расходы IsActive: False


Оказывается, что моя функция SetProperty очень похожа на функцию ForEach, которая уже встроена в фреймворк. Главное отличие состоит в том, что шахта может работать на любом IEnumerable<T>. Этот синтаксис любят одни и ненавидят другие по причинам, которые Эрик Липперт указал в своем блоге (спасибо Джону скиту за указание на это). Также видеть это Обсуждение командой Microsoft. Я оставляю вам сделать свой собственный вывод.

На боковой ноте, называя его SetProperty является своего рода неточным, потому что вы могли бы сделать любое действие на предметы в коллекции. Вы можете назвать его ForEach, но это противоречит структуре. Не уверен, как бы я это назвал, но, возможно, PerformAction.

Следующая инструкция Linq должна работать

MyProjects.Projects.Where(p => p.Name == "Overhead")
.Select(x => {x.IsActive = true; return x;})
.ToList();

Согласно комментариям @JonSkeet, @TimSchmelter и @LeandroSoares, приведенный выше код действительно плохая идея. Вот некоторые причины

  1. Вызов .ToList() вызывает выполнение и загрузку в память всей ленивой коллекции.
  2. Приведенный выше код не очень удобочитаем и его трудно поддерживать. Приведенный выше код злоупотребляет API, поскольку он заставляет API делать вещи, для которых он не предназначен.

Comments

    Ничего не найдено.