Объединение нескольких списков в С#
Я ищу элегантное решение для следующей ситуации:
У меня есть класс, который содержит List, например
class MyClass{
...
public List<SomeOtherClass> SomeOtherClassList {get; set;}
...
}
Третий класс, называемый Model
, содержит a List<Myclass>
, который является тем, с которым я работаю extern.
Теперь я хотел бы расширить класс Model с помощью метода, который возвращает все уникальные экземпляры SomeOtherClass во всех экземплярах MyClass.
Я знаю, что существует метод Union()
и с циклом foreach я могу легко решить эту проблему, что я и сделал. Однако, поскольку я новичок во всех функциях С# 3 +, мне любопытно, как это можно было бы сделать более элегантно, с Linq или без него.
Я нашел подход, который мне кажется довольно неуклюжим, но он работает:
List<SomeOtherClass> ret = new List<SomeOtherClass>();
MyClassList.Select(b => b.SomeOtherClasses).ToList().ForEach(l => ret = ret.Union(l).ToList());
return ret;
Примечание. Свойство b.SomeotherClasses
возвращает a List<SomeOtherClasses>
.
Этот код далек от совершенства, и некоторые вопросы возникают из-за того, что мне нужно выяснить, что такое хороший стиль для работы с С# 3, а что нет. Итак, я сделал небольшой список с мыслями об этом фрагменте, и я был бы рад получить несколько комментариев. Кроме того, я был бы рад услышать некоторые комментарии о том, как улучшить этот код.
- Временный список
ret
, возможно, был бы частью подхода на С# 2, но верно ли, что я должен уйти в отставку из этого списка с использованием метода цепочки? Или мне не хватает точки?
- Действительно ли требуется использовать промежуточный метод
ToList()
? Все, что я хочу, - это выполнить еще одно действие для каждого члена.
- Какова стоимость этих операций ToList()? Хороший стиль? Необходимо?
Спасибо.
Ответы
Ответ 1
Вы ищете SelectMany()
+ Distinct()
:
List<SomeOtherClass> ret = MyClassList.SelectMany( x => x.SomeOtherClasses)
.Distinct()
.ToList();
SelectMany()
сгладит "список списков" в один список, тогда вы можете просто выделить отдельные записи в этом перечислении, а не использовать объединение между отдельными подписями.
В общем, вы захотите избежать побочных эффектов с помощью Linq, ваш оригинальный подход - это злоупотребление этим изменением ret
, которое не является частью запроса.
ToList()
требуется, поскольку каждый стандартный оператор запроса возвращает новое перечисление и не изменяет существующее перечисление, поэтому вам нужно преобразовать итоговый результат перечисления в список. Стоимость ToList()
- это полная итерация перечисления, которая в большинстве случаев пренебрежимо мала. Конечно, если ваш класс может использовать IEnumerable<SomeOtherClass>
вместо этого, вам не нужно полностью преобразовывать его в список.
Ответ 2
Вы должны посмотреть SelectMany. Что-то вроде этого должно генерировать ваш "плоский" список:
MyClassList.SelectMany(b => b.SomeOtherClasses)
Он вернет IEnumerable<SomeOtherClass>
, который вы можете фильтровать/обрабатывать дальше.