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

时间:2013-12-08 13:08:40

标签: c# ienumerable

我有这个功能:

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

6 个答案:

答案 0 :(得分: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;

答案 1 :(得分: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;
    });
}

答案 2 :(得分: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 */);

答案 3 :(得分: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 */);

答案 4 :(得分:0)

这是使用LINQ的扩展方法的简单版本:

isTranslucent

答案 5 :(得分:0)

正如其他人所说,Select 总是为每个输入元素产生一个输出元素,但如果您的情况是根据类型进行过滤,那么您可以使用 Enumerable.OfType<TResult>(IEnumerable) - https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.oftype?view=net-5.0

例如这个:

var agents = data.Where(d => d is Agent).Select(d => d as Agent);

相当于:

var agents = data.OfType<Agent>();