65.9K
CodeProject 正在变化。 阅读更多。
Home

以通用方式实现 Continuation - C# 中 Option 类型的用法 - 使代码更清晰

starIconstarIconstarIconstarIconstarIcon

5.00/5 (2投票s)

2011 年 8 月 23 日

CPOL

4分钟阅读

viewsIcon

23891

downloadIcon

288

C# 中的 Continuation - 使代码更清晰。

引言

需要解决的问题:如果你看很多 C# 代码,代码必须从上到下阅读,遵循所有的 IfElseTry 和其他分支才能真正理解发生了什么。此外,很多代码在技术上都很普通,例如检查 null 值。我想知道是否可以编写从左到右阅读的代码,并且原则上是非技术性的。它看起来像一系列有意义的操作。F# 中有一个‘类似’的概念称为管道(pipelining)。在 F# 中,它看起来像这样:

Let squared =
  Numbers
 |> List.filter isOdd
 |> List map square;;

对象 Numbers 被通过后续的函数进行管道化。在 C# 中,我想做类似的事情:

[SomeObject].AndThen(isValid)
  . AndThen(Save)
  . AndThenIfElse(Do_this_If_ThisIsTrue, Do_this_IfOtherwise)

等等。在这种情况下,SomeObject 被传递给函数 IsValid,然后传递给 Save,再传递给 Do_this_If_ThisIsTrue,该函数可以返回 'false',在这种情况下,SomeObject 被传递给 Do_this_ifOtherWise。这种链式调用的好处是我们可以根据需要添加任意数量的 AndThenAndThenIfElse

多年前,你可以使用委托(delegates)来实现这一点。委托最终不是通用的。现在我们可以使用它们,但我们也想处理错误处理和 null 类似的代码。Tomas Petricek 和 John Skeet 的《Real-World Functional Programming》提供了解决方案。秘诀在于两个主要方面:Option 类型和仅使用一个固定的接口:(func of Option of T, Option of T),这意味着你传递一个接收 Option 并返回一个 Option 的函数。这个接口可以根据需要进行链式调用或管道化。

Option 类型是 F# 中一个广为人知的类型。一个 Option 可以是 NoneSomeSome 封装了一个实际的业务对象。Continuation 是将函数的结果传递给另一个函数的概念。

如果你将这两者结合起来会怎么样?这里涉及很多理论,但我不会让你烦恼。如果你将一个对象包装在 Option 中,然后使用 Continuation,你可以像上面 AndThen 代码所示的那样继续链式调用。Option 对象隐藏了其中的实际对象类型。

Option 内部可以包含任何东西。利用 Option 概念,我们可以让强类型语言以通用方式运行。

对于 continuation,你可以使用 Lambda 表达式,但这样的话类型系统将不允许你以通用方式实现它们。

本文旨在实现通用方式的 Continuation。

Option 类型是一个 abstract 类,有两个具体实现:NoneSomeNone 结束链式调用,Some 封装了 [SomeObject]。函数必须先获取封装的对象,对其进行一些处理,然后返回 None 或一个包含封装对象的 SomeOption SomeNone 类是泛型的!

在这个 Option 类型中,我添加了一些函数,例如上面的 AndThenIfElse。如果你查看参数,你会看到你传递了以下两个参数:Func of Option of T, Option of T tryFirst, Func of Option of T, Option of T tryNext,这意味着你传递了两个函数。

我添加了

  1. AndThen (continuation)。如果前一个函数返回 Some,则继续执行下一个函数。
  2. AndThenCheck。你将一个接收 Option 并返回 Boolean 的函数作为第一个参数传递。如果这个 Booleantrue,则执行第二个参数,这是一个 continuation。如果不是 true,则返回 None。
  3. AndThenCaseCheck。你传递一个 AndThenCheck 列表。如果某个检查返回 true,则执行伴随的 continuation。如果没有检查返回 true,则返回 None。在附带的示例中,我使用 Lambda 表达式来组合这个列表。Funcs 可以被 Lambda 表达式替换,在这种情况下,我认为这是一个好主意。
  4. AndThenIfElse。你传递两个函数。如果第一个返回 None,则执行第二个 continuation。

另一个值得注意的有趣之处是,上面的 continuation 管道直到调用 Exec() 才会被执行。这样做的原因是,异常仅在 Exec() 调用中处理,并且 continuation 在遇到第一个 None 返回时停止。管道实际上是一个 MultiCastDelgates 的 ArrayList,可以进行序列化。Option/Continuation 概念也可以用于其他场景,例如通过保存执行直到触发。

我添加了一个如何在实际中使用它的具体示例。代码本身并没有做什么,但能让你很好地了解如何使用它。查看下面的代码(ClientController),你会看到它的真正好处。

someClientData.AndThen(isValid)
    .AndThen(ClientBusiness.Save)
    .AndThenIfElse(ClientBusiness.CheckOnDustin, ClientBusiness.CheckOnNotDustin)
    .AndThenCheck(ClientBusiness.ANameLengthCheck, ClientBusiness.GetLastOneInLocality)
    .AndThenCaseCheck(ClientBusiness.SomeCaseListOnProfession())
    .Exec();

代码是从左到右阅读的吗?它看起来像一系列链式操作吗?它是否“隐藏”了技术处理,而侧重于业务处理?你认为呢?告诉我。

历史

  • 2011年8月23日:初版
© . All rights reserved.