Linq для объектов - выберите первый объект

Я почти ничего не знаю о 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();

который кажется мне несколько уродливым, а также генерирует исключение, если нет соответствующих процессов. Есть ли лучший способ?

UPDATE

Я на самом деле пытаюсь найти первый соответствующий элемент и называть 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

Ответ 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);

Ответ 2

Не используйте Count(), как говорит ICR. Count() будет проходить через IEnumerable, чтобы выяснить, сколько у него элементов. В этом случае штраф за исполнение может быть незначительным, так как не так много процессов, но это плохая привычка. Используйте Count() только в том случае, если ваш запрос интересует только количество результатов. Count почти никогда не является хорошей идеей.

Есть несколько проблем с ответом FryHard. Во-первых, из-за отложенное выполнение, вы в конечном итоге выполните запрос 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 );
}