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