Может ли IEnumerable.Select() пропускать элемент?
У меня есть эта функция:
public IEnumerable<string> EnumPrograms() {
return dev.AudioSessionManager2.Sessions.AsEnumerable()
.Where(s => s.GetProcessID != 0)
.Select(s => {
try {
return Process.GetProcessById((int)s.GetProcessID).ProcessName;
}
catch (ArgumentException) {
return null;
}
});
}
Файл try..catch необходим, поскольку могут быть сеансы с PID, который больше не существует. Я бы хотел пропустить их. Есть ли способ сделать это из обратного вызова Select
или мне нужно добавить новое условие Where
, которое пропускает значения null
?
Ответы
Ответ 1
Нет, Select
всегда дает один выходной элемент для каждого элемента ввода. Нет альтернативы этому. Вы можете легко написать собственный метод расширения FilteredSelect
- но проще просто использовать предложение Where
.
В качестве альтернативы используйте Process.GetProcesses()
, чтобы получить моментальный снимок всех процессов, а затем присоедините его к коллекциям сеансов (или используйте что-то подобное). Это позволит избежать уродливого улова:
var sessionProcessIds = new HashSet<int>(dev.AudioSessionManager2.Sessions
.AsEnumerable()
.Select(x => x.GetProcessId)
.Where(pid => pid != 0));
var processes = Process.GetProcesses();
var sessionProcessNames = processes.Where(p => sessionProcessIds.Contains(p.Id))
.Select(p => p.ProcessName);
Или:
var names = from session in dev.AudioSessionManager2.Sessions.AsEnumerable()
let pid = session.GetProcessId
where pid != 0
join process in Process.GetProcesses() on pid equals process.Id
select process.ProcessName;
Ответ 2
Select
не может это сделать сам по себе, вы можете создать собственный метод расширения для него, как упомянуто @Jon Skeet.
public static IEnumerable<TResult> FilteredSelect<TSource, TResult>(
this IEnumerable<TSource> source
, Func<TSource, bool> predicate
, Func<TSource, TResult> selector)
{
foreach (var item in source)
{
if (predicate(item))
{
yield return selector(item);
}
}
}
И используйте как
elements.FilteredSelect(/* where condition */, /* select values */);
Ответ 3
Select
в Linq эквивалентен Map
, а Aggregate
эквивалентен Reduce
. Карта/Выбор - вход 1:1 для вывода. Вы хотите использовать Reduce
/Aggregate
, если нет отношения 1:1.
public IEnumerable<string> EnumPrograms() {
return dev.AudioSessionManager2.Sessions.AsEnumerable()
.Where(s => s.GetProcessID != 0)
.Aggregate(new List<string>(), (acc, s) => {
try {
var proc = Process.GetProcessById((int)s.GetProcessID).ProcessName;
acc.Add(proc);
} catch (ArgumentException) { }
return acc;
});
}