C# 试着用流利的表达来捕捉

C# 试着用流利的表达来捕捉,c#,linq,select,exception-handling,functional-programming,C#,Linq,Select,Exception Handling,Functional Programming,此LINQ查询表达式失败,Win32Exception“访问被拒绝”: 此操作失败,出现IOException“设备未准备就绪”: 过滤掉不可访问对象并避免异常的最佳方法是什么?插入WHERE过滤器(尝试访问任何对象并吸收可能的访问错误),包括: 第一句话: Process .GetProcesses() .Where(p => { try { var x = p.MainModule; return true; } catch { return false; } })

此LINQ查询表达式失败,Win32Exception“访问被拒绝”:

此操作失败,出现IOException“设备未准备就绪”:

过滤掉不可访问对象并避免异常的最佳方法是什么?

插入WHERE过滤器(尝试访问任何对象并吸收可能的访问错误),包括:

第一句话:

Process
   .GetProcesses()
   .Where(p => { try { var x = p.MainModule; return true; } catch { return false; } })
   .Select(p => p.MainModule.FileName)
DriveInfo
   .GetDrives()
   .Where(d => { try { var x = d.VolumeLabel; return true; } catch { return false; } })
   .Select(d => d.VolumeLabel)
第二句话:

Process
   .GetProcesses()
   .Where(p => { try { var x = p.MainModule; return true; } catch { return false; } })
   .Select(p => p.MainModule.FileName)
DriveInfo
   .GetDrives()
   .Where(d => { try { var x = d.VolumeLabel; return true; } catch { return false; } })
   .Select(d => d.VolumeLabel)

我会尝试第一种情况:

//Declare logger type
private readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);

Process.GetProcesses()
.Where(p => { 
    try {
        var x = p.MainModule;
        return true;
    }
    catch(Win32Exception e2)
    { IgnoreError(); } 
    })
.Select(p => p.MainModule.FileName)

public static void IgnoreError(Exception e) 
{
    #if DEBUG
    throw e2;
    //Save the error track, I prefer log4net
    _log.Info("Something bad happened!");
    #end if
}
对于第二种情况,我更愿意使用IF并保存日志:

//Somewhere in the begging of your class, in a place whose name I do not care to remember ...
//Declare logger type
private readonly ILog _log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);


public List<string> VolumenLabels()
{
    //Return the List<T>
    List<string> myVolumeLabels = new List<string>();
    //Getting the info
    DriveInfo[] allDrives = DriveInfo.GetDrives();

    foreach(DriveInfo drive in allDrives)
    {
        if (drive.IsReady == true)
        {
            myVolumeLabels.Add(drive.VolumeLabel.ToString());
        }
        else
        {
            _log.Info("Check the Drive: " + drive.Name + " the device is not ready.");
        }
    }    
    return myVolumeLabels;
}
//在你们班乞讨的地方,在一个我不想记住名字的地方。。。
//声明记录器类型
private readonly ILog\u log=LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
公共列表卷标签()
{
//返回列表
List myVolumeLabels=新列表();
//获取信息
DriveInfo[]allDrives=DriveInfo.GetDrives();
foreach(所有驱动器中的DriveInfo驱动器)
{
如果(drive.IsReady==true)
{
添加(drive.VolumeLabel.ToString());
}
其他的
{
_log.Info(“检查驱动器:“+Drive.Name+”设备未就绪。”);
}
}    
返回myvolumelabel;
}

我希望,我帮了一点忙。。。祝你今天愉快

编写一个扩展方法

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;
        }
    }
}
void Main()
{
变量volumeLabels=
DriveInfo
.GetDrives()
.SelectSafe(dr=>dr.VolumeLabel);
}
//在此处定义其他方法和类
公共静态类LinqExtensions
{
公共静态IEnumerable SelectSafe(此IEnumerable源,Func选择器)
{
foreach(源中的var项)
{
T2值=默认值(T2);
尝试
{           
值=选择器(项目);
}
抓住
{
继续;
}
收益回报值;
}
}
}

通过这种方式,您可以定制您想要的任何行为,并且您不必创建笨重的where子句,这样您甚至可以在出现异常时让它返回一个可选值。

您的答案是正确的。当然,您可以尝试在扩展方法中隐藏检查逻辑

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)
公共静态IEnumerable WhereSafe(此IEnumerable序列,Func选择器)
{
foreach(序列中的var元素)
{
试试{选择器(元素);}
捕获{继续;}
收益-收益要素;
}
}
过程
.getProcesss()
.WhereSafe(p=>p.main模块)
.Select(p=>p.MainModule.FileName)
或者更好:

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)
公共静态IEnumerable TrySelect(此IEnumerable序列,函数选择器)
{
TInner电流=默认值(TInner);
foreach(序列中的var元素)
{
试试{current=选择器(元素);}
捕获{继续;}
产生回流电流;
}
}
过程
.getProcesss()
.TrySelect(p=>p.MainModule.FileName)
基于注释更新:此解决方案不适用于常见枚举数。它是根据问题示例中使用的枚举数工作的。因此,它不是一个通用的解决方案。因为它是作为一个通用解决方案编写的,所以我建议不要使用它(为了保持简单)。我将保留此答案以丰富知识库

另一种扩展方法解决方案。为什么我更喜欢它而不是现有的解决方案

  • 我们只想跳过导致异常的元素。这是我们LINQ扩展的唯一关注点
  • 此实现不会将
    Select
    try/catch
    的问题混合在一起
  • 我们仍然可以在需要时使用现有的LINQ方法,如
    Select
  • 它是可重用的:它允许在LINQ查询中使用多种方法
  • 它遵循linq命名约定:我们实际上跳过了类似于
    skip
    SkipWhile
    的方法
用法:

var result = DriveInfo
    .GetDrives()
    .Select(d => d.VolumeLabel)
    .SkipExceptions() // Our extension method
    .ToList();
代码:

公共静态类EnumerableText
{
//我们使用'Skip'名称是因为它的隐含行为等于'Skip'和'SkipWhile'实现
公共静态IEnumerable SkipExceptions(此IEnumerable源)
{
//我们使用枚举器在枚举源时能够捕获异常
使用(var enumerator=source.GetEnumerator())
{
//我们使用带中断的true循环,因为enumerator.MoveNext可以引发我们需要处理的异常
while(true)
{
var exceptionCaught=假;
var currentElement=默认值(TSource);
尝试
{
如果(!enumerator.MoveNext())
{
//我们已完成枚举。请中断以退出while循环
打破
}
currentElement=枚举数。当前;
}
抓住
{
//忽略此异常并跳过此项目。
exceptionCaught=true;
}
//如果捕获到异常,请跳过此项。否则返回当前元素。
如果(例外情况)继续;
收益率-收益率要素;
}
}
}
}

这更像是您在进行测试。。。你在4分钟内回答了自己的问题。如果这确实是一个正确的答案,那么它应该是一个维基百科。我到处寻找,找到了我自己的选择,但不确定这是最好的。@nixbb回答你自己的问题是完全合法的。SO甚至建议扩展知识库。为了保持简单,显然没有好的通用解决方案,这似乎是正确的答案:)对不起,这根本没有帮助。为什么这比答案好?我不认为这更好,这只是另一种选择,还有一个优点,就是使用
var result = DriveInfo
    .GetDrives()
    .Select(d => d.VolumeLabel)
    .SkipExceptions() // Our extension method
    .ToList();
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;
            }
        }
    }
}