【问题标题】:Can IEnumerable.Select() skip an item?IEnumerable.Select() 可以跳过一个项目吗?
【发布时间】:2013-12-08 13:08:39
【问题描述】:

我有这个功能:

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


    【解决方案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;
    

    【讨论】:

    • 第二个建议听起来很有趣。但是带有音频会话的进程只是正在运行的进程的一小部分,所以我想它的效率会低一些(在这种情况下并不重要)?
    • @ThiefMaster:我不知道Process.GetProcesses() 需要多长时间,但至少你只会调用它一次,而不是每次会话一次。
    • @ThiefMaster 我很确定GetProcessesGetProcessByID 都在后台调用EnumProcesses
    【解决方案2】:

    以 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 => e.FirstName, e => e.Age>25);
    

    【讨论】:

      【解决方案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;
          });
      }
      

      【讨论】:

        【解决方案4】:

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

        【讨论】:

          【解决方案5】:

          这是使用 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));
          }
          

          【讨论】:

            【解决方案6】:

            正如其他人所说,Select 总是为每个输入元素生成一个输出元素,但如果您的情况是根据类型进行过滤,那么您可以使用 Enumerable.OfType&lt;TResult&gt;(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>();
            

            【讨论】:

              猜你喜欢
              • 2023-04-09
              • 2023-03-10
              • 1970-01-01
              • 2011-05-31
              • 2012-07-26
              • 1970-01-01
              • 2016-01-24
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多