C# IEnumerable.Select()是否可以跳过项目?

C# IEnumerable.Select()是否可以跳过项目?,c#,ienumerable,C#,Ienumerable,我有这个功能: public IEnumerable<string> EnumPrograms() { return dev.AudioSessionManager2.Sessions.AsEnumerable() .Where(s => s.GetProcessID != 0) .Select(s => { try { return Process.GetProcessByI

我有这个功能:

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;
            }
        });
}
public IEnumerable枚举程序(){
返回dev.AudioSessionManager2.Sessions.AsEnumerable()
.Where(s=>s.GetProcessID!=0)
。选择(s=>{
试一试{
返回Process.GetProcessById((int)s.GetProcessID).ProcessName;
}
捕获(异常){
返回null;
}
});
}

try..catch是必需的,因为可能存在PID不再存在的会话。我想跳过它们。是否有办法从
Select
回调中执行此操作,或者是否需要添加一个新的
Where
条件以跳过
null
值?

否,
Select
始终为每个输入元素生成一个输出元素。除此之外别无选择。您可以很容易地编写自己的
FilteredSelect
扩展方法,但使用
Where
子句更简单

或者,使用
Process.getprocesss()
获取所有进程的快照,然后将其加入会话集合(或使用类似的方法)。这将避免丑陋的陷阱:

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

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

Linq中的
Select
相当于
Map
,而
Aggregate
相当于
Reduce
。映射/选择是1:1输入到输出。如果不存在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;
    });
}
public IEnumerable枚举程序(){
返回dev.AudioSessionManager2.Sessions.AsEnumerable()
.Where(s=>s.GetProcessID!=0)
.Aggregate(新列表(),(acc,s)=>{
试一试{
var proc=Process.GetProcessById((int)s.GetProcessID).ProcessName;
acc.Add(程序);
}捕获(参数异常){}
返回acc;
});
}

基于John Skeet的帖子,这种扩展方法为我节省了无数行代码。名称非常适合选择where。下面列出的代码是您可以使用的扩展方法

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

下面是使用LINQ的扩展方法的更简单版本:

public static IEnumerable<TResult> FilteredSelect<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, Func<TSource, TResult> selector)
{
    return source
        .Where(item => predicate(item))
        .Select(item => selector(item));
}
public static IEnumerable FilteredSelect(此IEnumerable源、Func谓词、Func选择器)
{
返回源
.其中(项=>谓词(项))
.选择(项目=>选择器(项目));
}

正如其他人所说的
Select
总是为每个输入元素生成一个输出元素,但是如果您的案例是基于类型进行过滤,那么您可以使用
可枚举的类型(IEnumerable)
-

例如:

var-agents=data。其中(d=>d是代理)。选择(d=>d作为代理);
相当于:

var-agents=data.OfType();

第二个建议听起来很有趣。但是带有音频会话的进程只是正在运行的进程的一小部分,所以我想它的效率会稍低一些(在这种情况下这并不重要)?@ThiefMaster:我不知道
Process.GetProcesses()
需要多长时间,但至少你只能调用它一次,而不是每次会话调用一次。@ThiefMaster我很确定
getProcesss
GetProcessByID
都在幕后调用
enumProcesss
    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 => e.FirstName, e => e.Age>25);
public static IEnumerable<TResult> FilteredSelect<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, bool> predicate, Func<TSource, TResult> selector)
{
    return source
        .Where(item => predicate(item))
        .Select(item => selector(item));
}