如前所述,您几乎在这里实现了 Monad。
您的代码有点不雅,因为 lambdas 有副作用。单子更优雅地解决了这个问题。
那么,为什么不把你的代码变成一个合适的 Monad 呢?
奖励:您可以使用 LINQ 语法!
有请:
LINQ 到结果
例子:
var result =
from a in SomeThingHappensHere()
let someData = a.Data
from b in SomeOtherThingHappensHere(someData)
from c in AndYetAnotherThing()
from d in AndOneMoreThing(someData)
select d;
HandleTheFinalResultHere(result.Value);
使用LINQ to Results,这首先执行SomeThingHappensHere
。如果成功,它将获取Data
结果属性的值并执行SomeOtherThingHappensHere
。如果成功,则执行AndYetAnotherThing
,依此类推。
如您所见,您可以轻松链接操作并参考先前操作的结果。每个操作都会一个接一个地执行,遇到错误就会停止执行。
每行的from x in
位有点嘈杂,但 IMO 没有什么比这更易读的了!
我们如何进行这项工作?
C# 中的 Monad 由三部分组成:
你需要做的就是创造一些看起来像 Monad、感觉像 Monad、闻起来像 Monad 的东西,一切都会自动运行。
LINQ to Results的类型和方法如下。
结果<T> 类型:
表示结果的简单类。结果要么是T类型的值,要么是错误。可以从T或Exception构造结果。
class Result<T>
{
private readonly Exception error;
private readonly T value;
public Result(Exception error)
{
if (error == null) throw new ArgumentNullException("error");
this.error = error;
}
public Result(T value) { this.value = value; }
public Exception Error
{
get { return this.error; }
}
public bool IsError
{
get { return this.error != null; }
}
public T Value
{
get
{
if (this.error != null) throw this.error;
return this.value;
}
}
}
扩展方法:
Select
和SelectMany
方法的实现。方法签名在 C# 规范中给出,所以您只需要担心它们的实现。如果您尝试以有意义的方式组合所有方法参数,这些会很自然地出现。
static class ResultExtensions
{
public static Result<TResult> Select<TSource, TResult>(this Result<TSource> source, Func<TSource, TResult> selector)
{
if (source.IsError) return new Result<TResult>(source.Error);
return new Result<TResult>(selector(source.Value));
}
public static Result<TResult> SelectMany<TSource, TResult>(this Result<TSource> source, Func<TSource, Result<TResult>> selector)
{
if (source.IsError) return new Result<TResult>(source.Error);
return selector(source.Value);
}
public static Result<TResult> SelectMany<TSource, TIntermediate, TResult>(this Result<TSource> source, Func<TSource, Result<TIntermediate>> intermediateSelector, Func<TSource, TIntermediate, TResult> resultSelector)
{
if (source.IsError) return new Result<TResult>(source.Error);
var intermediate = intermediateSelector(source.Value);
if (intermediate.IsError) return new Result<TResult>(intermediate.Error);
return new Result<TResult>(resultSelector(source.Value, intermediate.Value));
}
}
您可以自由修改 Result<T> 类和扩展方法,例如,实现更复杂的规则。只有扩展方法的签名必须与所述完全一致。