Cri*_* S. 11 c# linq select functional-programming exception-handling
此LINQ查询表达式失败,出现Win32Exception" 访问被拒绝 ":
Process.GetProcesses().Select(p => p.MainModule.FileName)
Run Code Online (Sandbox Code Playgroud)
这会因IOException" 设备未就绪 " 而失败:
DriveInfo.GetDrives().Select(d => d.VolumeLabel)
Run Code Online (Sandbox Code Playgroud)
过滤掉无法访问的对象并避免异常的最佳方法是什么?
Cli*_*int 13
写一个扩展方法!
void Main()
{
var volumeLabels =
DriveInfo
.GetDrives()
.SelectSafe(dr => dr.VolumeLabel);
}
// Define other methods and classes here
public static class LinqExtensions
{
public static IEnumerable<T2> SelectSafe<T,T2>(this IEnumerable<T> source, Func<T,T2> selector)
{
foreach (var item in source)
{
T2 value = default(T2);
try
{
value = selector(item);
}
catch
{
continue;
}
yield return value;
}
}
}
Run Code Online (Sandbox Code Playgroud)
通过这种方式,您可以自定义您想要的任何行为,并且您不必创建笨重和hacky where子句,这样您甚至可以在有异常的情况下让它返回替代值.
Aph*_*ion 10
基于注释更新:此解决方案不适用于常见的枚举器.它确实基于问题示例中使用的枚举器.因此,它不是通用的解决方案.因为它是作为通用解决方案编写的,所以我建议不要使用它(为了简单起见).我将保留这个答案,以丰富知识库.
另一种Extension方法解决方案 为什么我更喜欢它而不是现有的解决方案?
Select和try/catch.Select在需要时使用现有的LINQ方法.Skip和SkipWhile方法.用法:
var result = DriveInfo
.GetDrives()
.Select(d => d.VolumeLabel)
.SkipExceptions() // Our extension method
.ToList();
Run Code Online (Sandbox Code Playgroud)
码:
public static class EnumerableExt
{
// We use the `Skip` name because its implied behaviour equals the `Skip` and `SkipWhile` implementations
public static IEnumerable<TSource> SkipExceptions<TSource>(this IEnumerable<TSource> source)
{
// We use the enumerator to be able to catch exceptions when enumerating the source
using (var enumerator = source.GetEnumerator())
{
// We use a true loop with a break because enumerator.MoveNext can throw the Exception we need to handle
while (true)
{
var exceptionCaught = false;
var currentElement = default(TSource);
try
{
if (!enumerator.MoveNext())
{
// We've finished enumerating. Break to exit the while loop
break;
}
currentElement = enumerator.Current;
}
catch
{
// Ignore this exception and skip this item.
exceptionCaught = true;
}
// Skip this item if we caught an exception. Otherwise return the current element.
if (exceptionCaught) continue;
yield return currentElement;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
你的答案是正确的.您当然可以尝试在扩展方法中隐藏检查逻辑.
public static IEnumerable<TElement> WhereSafe<TElement, TInner>(this IEnumerable<TElement> sequence, Func<TElement, TInner> selector)
{
foreach (var element in sequence)
{
try { selector(element); }
catch { continue; }
yield return element;
}
}
Process
.GetProcesses()
.WhereSafe(p => p.MainModule)
.Select(p => p.MainModule.FileName)
Run Code Online (Sandbox Code Playgroud)
或者更好:
public static IEnumerable<TInner> TrySelect<TElement, TInner>(this IEnumerable<TElement> sequence, Func<TElement, TInner> selector)
{
TInner current = default(TInner);
foreach (var element in sequence)
{
try { current = selector(element); }
catch { continue; }
yield return current;
}
}
Process
.GetProcesses()
.TrySelect(p => p.MainModule.FileName)
Run Code Online (Sandbox Code Playgroud)
插入WHERE过滤器(尝试访问任何对象并吸收可能的访问错误):
{ try { var x = obj.MyProp; return true; } catch { return false; } }:
Run Code Online (Sandbox Code Playgroud)
第一个表达:
Process
.GetProcesses()
.Where(p => { try { var x = p.MainModule; return true; } catch { return false; } })
.Select(p => p.MainModule.FileName)
Run Code Online (Sandbox Code Playgroud)
第二个表达:
DriveInfo
.GetDrives()
.Where(d => { try { var x = d.VolumeLabel; return true; } catch { return false; } })
.Select(d => d.VolumeLabel)
Run Code Online (Sandbox Code Playgroud)