【问题标题】:Linq to objects - select first objectLinq to objects - 选择第一个对象
【发布时间】:2010-09-05 16:04:33
【问题描述】:

我对 linq 几乎一无所知。

我正在这样做:

var apps = from app in Process.GetProcesses()
    where app.ProcessName.Contains( "MyAppName" ) && app.MainWindowHandle != IntPtr.Zero
    select app;

这让我找到了符合该标准的所有正在运行的进程。

但我不知道如何获得第一个。我在网上找到的例子似乎暗示我必须这样做

var matchedApp = (from app in Process.GetProcesses()
    where app.ProcessName.Contains( "MyAppName" ) && app.MainWindowHandle != IntPtr.Zero
    select app).First();

这让我觉得有些难看,如果没有匹配的进程也会抛出异常。有没有更好的办法?

更新

我实际上是在尝试找到第一个匹配的项目,并在上面调用SetForegroundWindow

我想出了这个解决方案,它也让我觉得丑陋和糟糕,但比上面的要好。有什么想法吗?

var unused = from app in Process.GetProcesses()
    where app.ProcessName.Contains( "MyAppName" ) && app.MainWindowHandle != IntPtr.Zero
    select SetForegroundWindow( app.MainWindowHandle ); // side-effects in linq-query is technically bad I guess

【问题讨论】:

    标签: c# linq linq-to-objects


    【解决方案1】:

    @FryHard FirstOrDefault 将起作用,但请记住,如果没有找到它会返回 null。此代码未经测试,但应该接近您想要的:

    var app = Process.GetProcesses().FirstOrDefault(p => p.ProcessName.Contains("MyAppName") && p.MainWindowHandle != IntPtr.Zero);
    
    if (app == null)
        return;
    
    SetForegroundWindow(app.MainWindowHandle);
    

    【讨论】:

    • 你如何把它作为查询而不是扩展方法?
    • @Quintin FirstOrDefault 没有“关键字”语法 - 您必须使用扩展方法。
    • 嗯,你可以使用(query).FirstOrDefault(),但是扩展方法sentax更容易阅读imo
    • 我一直不明白为什么这么多人喜欢使用 SQL 语法而不是方法语法。太棒了。 SQL 的美妙之处在于它的速度,而不是它的语法!从语法上讲,SQL 是我一生中见过的最可怕的东西。 Quintin,虽然已经快三年了,如果您现在还没有使用扩展方法,请开始使用。
    【解决方案2】:

    不要像 ICR 所说的那样使用Count()。 Count() 将遍历 IEnumerable 以确定它有多少项目。在这种情况下,由于进程不多,性能损失可能可以忽略不计,但这是一个坏习惯。只有当您的查询只对结果的数量感兴趣时,才使用Count()。Count 几乎从来都不是一个好主意。

    FryHard 的回答存在几个问题。首先,由于delayed execution,您最终将执行两次LINQ 查询,一次获取结果数,一次获取FirstOrDefault。其次,在检查计数后没有任何理由使用FirstOrDefault。因为它可以返回 null,所以你不应该在不检查 null 的情况下使用它。要么做apps.First().MainWindowHandle要么:

    var app = apps.FirstOrDefault();
    
    if (app != null)
        SetForegroundWindow(app.MainWindowHandle);
    

    这就是为什么最好的解决方案是马克的,毫无疑问。这是使用 LINQ 获得所需内容的最有效和最稳定的方式。

    【讨论】:

      【解决方案3】:

      假设在您的第一个示例应用程序中是一个 IEnumerable,您可以使用 .Count 和 .FirstOrDefault 属性来获取要传递给 SetForegroundWindow 的单个项目。

      var apps = from app in Process.GetProcesses()
      where app.ProcessName.Contains( "MyAppName" ) && app.MainWindowHandle != IntPtr.Zero
      select app;
      
      if (apps.Count > 0)
      {
          SetForegroundWindow(apps.FirstOrDefault().MainWindowHandle );
      }
      

      【讨论】:

      • 如其他地方所述,这消除了 linq(延迟执行)的好处,并且 FirstOrDefault 是多余的,有一个计数
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-17
      • 1970-01-01
      • 2018-03-03
      • 1970-01-01
      • 1970-01-01
      • 2011-10-22
      • 1970-01-01
      相关资源
      最近更新 更多