IEnumerable.Select()可以跳过一个项目吗?

问题描述 投票:13回答:4

我有这个功能:

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条件?

c# ienumerable
4个回答
20
投票

不,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;

3
投票

Linq的Select相当于Map,而Aggregate相当于Reduce。 Map / Select是1:1输入到输出。如果没有1:1的关系,你想使用Reduce / Aggregate

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;
    });
}

1
投票

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 */);

0
投票

在John Skeet的帖子的基础上,这种扩展方法为我节省了无数的代码。这个名字非常适合SelectWhere。下面的代码清单是您可以使用的扩展方法。

    public static IEnumerable<TResult> SelectWhere<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector, Func<TSource, bool> predicate)
    {
        foreach (TSource item in source)
            if (predicate(item))
                yield return selector(item);
    }

用法:

entity.SelectWhere(e => /* returned element */, e => /* bool condition */);
© www.soinside.com 2019 - 2024. All rights reserved.