2013-12-08 11 views
11

Ich habe diese Funktion:Kann IEnumerable.Select() ein Element überspringen?

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

Die try..catch notwendig ist, da kann es Sitzungen mit einem PID, die nicht mehr existiert. Ich würde sie gerne überspringen. Gibt es eine Möglichkeit, dies vom Rückruf Select zu tun, oder muss ich eine neue Where Bedingung hinzufügen, die null Werte überspringt?

Antwort

15

Nein, Select liefert immer ein Ausgabeelement für jedes Eingabeelement. Es gibt keine Alternative dazu. Sie könnten einfach Ihre eigene FilteredSelect Erweiterungsmethode schreiben - aber es ist einfacher, nur eine Where Klausel zu verwenden.

Alternativ können Sie auch Process.GetProcesses() verwenden, um eine Momentaufnahme aller Prozesse zu erhalten und diese dann zu Ihrer Sitzungssammlung hinzuzufügen (oder etwas ähnliches zu verwenden). Das würde vermeiden, dass die hässlichen Haken:

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

Oder:

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; 
+0

Dieser zweite Vorschlag klingt interessant. Aber die Prozesse mit Audio-Sessions sind nur eine kleine Teilmenge der laufenden Prozesse, also denke ich, es wäre etwas weniger effizient (nicht, dass es in diesem Fall wichtig ist)? – ThiefMaster

+1

@ThiefMaster: Ich weiß nicht, wie lange 'Process.GetProcesses()' dauert, aber zumindest würden Sie es nur * einmal * anstatt einmal pro Sitzung aufrufen. –

+1

@ThiefMaster Ich bin mir ziemlich sicher, dass 'GetProcesses' und' GetProcessByID' 'EnumProcesses' unter der Haube aufrufen. – Rotem

1

Select kann das nicht von selbst, Sie eine benutzerdefinierte Erweiterungsmethode für das schaffen könnte als @ Jon Skeet erwähnt.

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

und Verwendung als

elements.FilteredSelect(/* where condition */, /* select values */); 
1

Select in Linq das Äquivalent zu Map ist, während Aggregate das Äquivalent zu Reduce ist. Map/Select ist ein 1: 1-Eingang zum Ausgang. Sie möchten Reduce/Aggregate verwenden, wenn keine 1: 1-Beziehung besteht.

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