滥用关闭?违反各种原则?或者可以吗?
编辑:修复了几个语法和一致性问题,使代码更加明显并接近我实际正在做的事情。
我有一些如下所示的代码:
SomeClass someClass;
var finalResult =
DoSomething(() =>
{
var result = SomeThingHappensHere();
someClass = result.Data;
return result;
})
.DoSomething(() => return SomeOtherThingHappensHere(someClass))
.DoSomething(() => return AndYetAnotherThing())
.DoSomething(() => return AndOneMoreThing(someClass))
.Result;
HandleTheFinalResultHere(finalResult);
其中 DoSomething
方法是一个扩展方法,并且它需要传入一个 Func 。因此,每个 DoSomething => 中的每个方法都调用lambda 返回 Result 类型。
这类似于 也许是单子。除了检查空值之外,我正在检查 Result 类的状态,并且调用传递给 DoSomething 的 Func 或返回前一个 Result 而不调用 Func
我面临的问题是想要在其中包含这种组合我的代码,但我还需要能够将数据从一个组合调用结果传递到另一个调用结果中,正如您在 someClass
变量中看到的那样。
我的问题不是这在技术上是否正确......我知道这是可行的,因为我目前正在这样做。我的问题是这是否滥用闭包,或命令查询分离,或任何其他原则......然后询问有什么更好的模式来处理这种情况,因为我相当确定我现在,这种类型的代码陷入了“闪亮的新锤子”模式。
Edit: fixed several syntax and consistency issues to make the code a little more apparent and close to what I actually am doing.
I've got some code that looks like this:
SomeClass someClass;
var finalResult =
DoSomething(() =>
{
var result = SomeThingHappensHere();
someClass = result.Data;
return result;
})
.DoSomething(() => return SomeOtherThingHappensHere(someClass))
.DoSomething(() => return AndYetAnotherThing())
.DoSomething(() => return AndOneMoreThing(someClass))
.Result;
HandleTheFinalResultHere(finalResult);
where the DoSomething
method is an extension method, and it expects a Func passed into it. So, each of the method calls in each of the DoSomething => lambda's returns a Result type.
this is similar to a Maybe monad. Except instead of checking for nulls, I am checking the status of the Result class, and either calling the Func that was passed into DoSomething or returning the previous Result without calling the Func
the problem i face is that want to have this kind of composition in my code, but i also need to be able to pass data from one of the composed call results into the call of another, as you can see with the someClass
variable.
My question isn't whether or not this is technically correct... i know this works, because I'm currently doing it. My question is whether or not this is an abuse of closures, or command-query separation, or any other principles... and then to ask what better patterns there are for handling this situation, because I'm fairly sure that I'm stuck in a "shiny new hammer" mode with this type of code, right now.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
正如已经指出的,您几乎已经在这里实现了 Monad。
您的代码有点不优雅,因为 lambda 具有副作用。 Monad 可以更优雅地解决这个问题。
那么,为什么不把你的代码变成一个合适的 Monad 呢?
额外的好处:你可以使用 LINQ 语法!
我介绍:
LINQ to Results
示例:
对于LINQ to Results,首先执行
SomeThingHappensHere
。如果成功,它将获取结果的Data
属性的值并执行SomeOtherThingHappensHere
。如果成功,它将执行AndYetAnotherThing
,依此类推。正如您所看到的,您可以轻松地链接操作并引用先前操作的结果。每一项操作都会依次执行,遇到错误就会停止执行。
每行的
from x in
位有点嘈杂,但在我看来,没有任何类似复杂性的东西比这更具可读性!我们如何做到这一点?
C# 中的 Monad 由三部分组成:
类型 Something-of-T,
Select
/SelectMany
扩展方法,以及将 T 转换为 Something-of-T
您所需要做的就是创建一些看起来像 Monad、感觉像 Monad、闻起来像 Monad 的东西,一切都会自动运行。
LINQ to Results 的类型和方法如下。
结果type:
表示结果的简单类。结果要么是 T 类型的值,要么是错误。结果可以从 T 或 Exception 构造。
扩展方法:
Select
和SelectMany
方法的实现。 C# 规范中给出了方法签名,因此您只需担心它们的实现。如果您尝试以有意义的方式组合所有方法参数,那么这些就会很自然地出现。您可以自由修改结果例如,类和扩展方法,以实现更复杂的规则。只有扩展方法的签名必须与规定的完全相同。
As has already been noted, you've almost implemented a Monad here.
Your code is a bit inelegant in that the lambdas have side-effects. Monads solve this more elegantly.
So, why not turn your code into a proper Monad?
Bonus: you can use LINQ syntax!
I present:
LINQ to Results
Example:
With LINQ to Results, this first executes
SomeThingHappensHere
. If that succeeds, it gets the value of theData
property of the result and executesSomeOtherThingHappensHere
. If that succeeds, it executesAndYetAnotherThing
, and so on.As you can see, you can easily chain operations and refer to results of previous operations. Each operation will be executed one after another, and execution will stop when an error is encountered.
The
from x in
bit each line is a bit noisy, but IMO nothing of comparable complexity will get more readable than this!How do we make this work?
Monads in C# consist of three parts:
a type Something-of-T,
Select
/SelectMany
extension methods for it, anda method to convert a T into a Something-of-T.
All you need to do is create something that looks like a Monad, feels like a Monad and smells like a Monad, and everything will work automagically.
The types and methods for LINQ to Results are as follows.
Result<T> type:
A straightforward class that represents a result. A result is either a value of type T, or an error. A result can be constructed from a T or from an Exception.
Extension methods:
Implementations for the
Select
andSelectMany
methods. The method signatures are given in the C# spec, so all you have to worry about is their implementations. These come quite naturally if you try to combine all method arguments in a meaningful way.You can freely modify the Result<T> class and the extension methods, for example, to implement more complex rules. Only the signatures of the extension methods must be exactly as stated.
在我看来,你已经在这里构建了与 monad 非常相似的东西。
您可以通过将委托类型设置为
Func
来使其成为适当的 monad,并通过某种方式设置要传入的初始SomeClass
值,并让DoSomething
将一个的返回值作为下一个的参数传递——这将使链接显式化,而不是依赖于词法作用域的共享状态。Looks to me like you've built something very similar to a monad here.
You could make it a proper monad by making your delegate type a
Func<SomeClass, SomeClass>
, have some way to set up the initialSomeClass
value to pass in, and haveDoSomething
pass the return value of one as the parameter of the next -- this would to make the chaining explicit rather than relying on lexically scoped shared state.该代码的弱点是第一个和第二个 lambda 之间的隐式耦合。我不确定修复它的最佳方法。
The weakness of this code is the implicit coupling between the first and second lambdas. I'm not sure of the best way to fix it.