C# 有没有一种方法可以只返回不抛出LINQ异常的函数调用?

C# 有没有一种方法可以只返回不抛出LINQ异常的函数调用?,c#,linq,C#,Linq,我被扔进了一个现有的代码库,工作的一部分是在我进行更新时逐步测试代码。因此,这是一个将旧的、丑陋的东西变得更好的过程 在这种情况下,有一个类似的代码: foreach (var thingamajigLocator in thingamajigLocators) { Thingamajig thingamajig; try { thingamajig = thingamajigservice.GetThingamajig(thingamajigLocato

我被扔进了一个现有的代码库,工作的一部分是在我进行更新时逐步测试代码。因此,这是一个将旧的、丑陋的东西变得更好的过程

在这种情况下,有一个类似的代码:

foreach (var thingamajigLocator in thingamajigLocators)
{
    Thingamajig thingamajig;
    try
    {
        thingamajig = thingamajigservice.GetThingamajig(thingamajigLocator);
    }
    catch
    {
        // exception logged further down, but is rethrown
        continue;
    }

    thingamajigCollection.Add(thingamajig);
}
这很难看,从理论上讲,如果异常被进一步处理,那么这里就没有必要处理它,但代码就是这样的,目前,处理服务代码的工作量太大了

我很想做这样的事情:

thingamajigCollection = thingamajigLocators
                            .Select(tl => thingamajigservice.GetThingamajig(tl))
                            .Where( /* some condition that strips out the ones throwing exceptions */ );

这有可能吗?还有其他建议吗?我当然可以把foreach留给try/catch,但它看起来可能更优雅,因为我不在乎这种情况下的实际异常是什么。同样,我知道这是一种可怕的形式,需要解决,但现在没有时间解决它。

我怀疑没有。任何方法最终都可能引发异常

我想知道,如果您接受并基本上忽略来自服务定位器的异常,那么您的异常到底是如何得到更高级别的处理的

我将实现一个,在出现错误时从您的服务定位器返回一个
NullThingamajig
,而不是捕获并接受异常。然后,您可以简单地使用以下查询筛选出结果:

var thingamajigs = thingamajigCollection.OfType<Thingamajig>();
然后您可以使用下面的完整查询:

var thingamajig = thingamajigLocators
    .Select(locator => service.GetThingamajig(new ThingamajigLocatorProxy(locator)))
    .OfType<Thingamajig>();
var thingamajig=thingamajig定位器
.Select(locator=>service.GetThingamajig(新ThingamajigLocatorProxy(locator)))
.of type();

您可以有一个静态助手方法,它可以满足您的需要:

public static KeyValuePair<T, Exception> TryExecute<T>(Func<T> func) {
    try {
        return new KeyValuePair<T, Exception>(func(), null);
    } catch (Exception ex) {
        return new KeyValuePair<T, Exception>(default(T), ex);
    }
}

thingamajigCollection = thingamajigLocators
                            .Select(tl => TryExecute(() => thingamajigservice.GetThingamajig(tl)))
                            .Where(p => p.Value is null)
                            .Select(p => p.Key));
public static KeyValuePair TryExecute(Func-Func){
试一试{
返回新的KeyValuePair(func(),null);
}捕获(例外情况除外){
返回新的KeyValuePair(默认值(T),ex);
}
}
thingamajigCollection=ThingamajigLocator
.Select(tl=>TryExecute(()=>thingamajigservice.GetThingamajig(tl)))
.其中(p=>p.值为空)
.选择(p=>p.Key));

这应该可以解决问题。。。如果需要,您仍然可以检查抛出的异常。如果您想让它变得更好,请创建一个自定义结构,而不是具有更合适的属性名称的
KeyValuePair
,但概念将保持不变。

下面的方法如何:

public IEnumerable<Thingamajig> GetThingamajigs()
{
    foreach (var thingamajigLocator in thingamajigLocators)
    {
        Thingamajig thingamajig;
        try
        {
            thingamajig = thingamajigservice.GetThingamajig(thingamajigLocator);
        }
        catch
        {
            // exception logged higher up
            // (how can this be? it just got swallowed right here)
            continue;
        }
        yield return thingamajig;

    }
}
public IEnumerable GetThingamajigs()
{
foreach(thingamajigLocator中的var thingamajigLocator)
{
Thingamajig Thingamajig;
尝试
{
thingamajig=thingamajigservice.GetThingamajig(thingamajigLocator);
}
抓住
{
//异常被记录到更高级别
//(这怎么可能?它就在这里被吞下了)
继续;
}
产量回报率;
}
}

然后,返回的IEnumerable可以在Linq语句中使用,并且原始代码的意图不会隐藏在太多的抽象中。

实际上,没有什么区别。Func只是一个委托。因此,您可以让它变得非常简单:

thingamajigCollection = thingamajigLocators
                        .Select(tl => 
                             {
                             try
                                 {
                                     return thingamajigservice.GetThingamajig(tl);
                                 }
                             catch(Exception)
                                 {
                                     return null;
                                 }
                             })
                          .Where(t1 => t1!=null);
作为一个选项,您可以在新方法中包装GetThingamajig以捕获异常


注意:正如hmemcpy所说,吞咽异常并不是最好的方法。因此,你最好尝试重新设计事物。

如果你想做得有点过头,相当干净地忽略例外,那么按照以下思路做点事情怎么样:

thingamajigCollection = 
    thingamajigLocators
    .Select(tl => F.Try(()=> thingamajigservice.GetThingamajig(tl)))
    .Where(thing=>thing.HasValue)
    .Select(thing=>thing.Value)
使用以下
static
helper类
F

public static Maybe<T> Try<T>(Func<T> f) {
    try {
        return Maybe<T>.FromValue(f());
    } catch (Exception e) {
        return Maybe<T>.FromException(e);
    }
}


public struct Maybe<T> {
    readonly T val;
    readonly Exception e;
    Maybe(T val, Exception e) { this.val = val; this.e = e; }

    public bool HasValue { get { return e == null; } }
    public T Value { 
        get {
            if (!HasValue) throw new InvalidOperationException("Attempted to get a value with an error", e); 
            else return val; 
        } 
    }
    public static Maybe<T> FromException(Exception e) { 
        return new Maybe<T>(default(T), e); 
    }
    public static Maybe<T> FromValue(T val) { 
        return new Maybe<T>(val, null); 
    }
}
public static可能会尝试(Func f){
试一试{
返回Maybe.FromValue(f());
}捕获(例外e){
可能返回。FromException(e);
}
}
可能是公共结构{
只读T值;
只读异常e;
可能(T val,例外e){this.val=val;this.e=e;}
public bool HasValue{get{return e==null;}}
公共T值{
得到{
如果(!HasValue)抛出新的InvalidOperationException(“试图获取一个有错误的值”,e);
否则返回val;
} 
}
公共静态可能来自异常(异常e){
返回新值(默认值(T),e);
}
公共静态值(T val){
返回新的Maybe(val,null);
}
}

只有我一个人认为foo是一个更好的标识符吗?我认为这个标识符太棒了!虽然由于它被混淆而很奇怪,但它通过将变量识别为有意义的东西来实现其目的,而不仅仅是
foo
list
。我应该说得低一点。异常会被记录并重试,但是当它进入我正在修改的代码时,它就会被忽略。那部分不是我的设计。我同意在理想的世界中,空对象或类似的解决方案会更好,但是现在深入研究服务代码不是一个选项,所以使用我所拥有的。啊,我明白了。那么我担心,如果没有将服务定位器包装到代理类中,在代理类中处理异常并返回null对象,那么您就很难接受异常……编辑很有意义。我选择了上面提到的简单方法变体,主要是因为它没有隐藏“hack”。当事情得到解决时,不应该以任何方式处理这一级别的异常,因此这有助于提醒我们这必须得到解决。对不起,我想我应该说得低一点。它会被记录下来,然后重新启动。是的,重新设计是一个持续的过程,但这是一个大型的10yo遗留系统,所以它会逐渐发生,一次发生一小部分。尽管我对自己的解决方案非常满意,当然,我怀疑这是最实用的——简短、易懂,还有一点让人难堪的是提醒自己,这是一种解决你无论如何都不想陷入的情况的方法:-)。顺便说一句,我更喜欢在这样的情况下将整个select语句写在一行上-它更清楚地说明了
public static Maybe<T> Try<T>(Func<T> f) {
    try {
        return Maybe<T>.FromValue(f());
    } catch (Exception e) {
        return Maybe<T>.FromException(e);
    }
}


public struct Maybe<T> {
    readonly T val;
    readonly Exception e;
    Maybe(T val, Exception e) { this.val = val; this.e = e; }

    public bool HasValue { get { return e == null; } }
    public T Value { 
        get {
            if (!HasValue) throw new InvalidOperationException("Attempted to get a value with an error", e); 
            else return val; 
        } 
    }
    public static Maybe<T> FromException(Exception e) { 
        return new Maybe<T>(default(T), e); 
    }
    public static Maybe<T> FromValue(T val) { 
        return new Maybe<T>(val, null); 
    }
}