F# 的血继限界(2)
某年某月,曾经某人希望 C# 出现 Java 匿名类的类似特性,然而最终他被 Java 的匿名类恶心到了。所谓的 Java 匿名类特性,即可以在项目里动态地创建一个类型,它继承了一个现有类,或者实现了一个现有接口。详情见《匿名类型的硬伤:围绕this的成员捕获策略》
很庆幸 F# 有“对象表达式”。它很优雅地实现了这个特性,而且不带副作用。对象表达式的好处,不仅仅在于,它避免了为处理特定情况而创建太多类型,同时因为它是内联的,因此它享受了闭包的好处,把函数式发挥到了极致。以下例子是个常用的手段,它巧妙的结合了对象表达式和 use/using 关键字。这个例子表达了一种需求:它临时改变环境之后,必须自动恢复原状,也可以扩展到更多应用。
open System
let changeColor clr =
let orig = Console.ForegroundColor
Console.ForegroundColor <- clr
// 用对象表达式返回一个实现了 IDisposable 接口的对象
{ new IDisposable with
member x.Dispose () =
Console.ForegroundColor <- orig
}
[<EntryPoint>]
let main argv =
Console.WriteLine "原来的颜色"
do use clr = changeColor ConsoleColor.Red
Console.WriteLine "变成红色"
Console.WriteLine "好贱,又变回来了"
Console.ReadLine () |> ignore
0
C# 里没有对象表达式,也没有其他“利用闭包动态实现子类”的可用方式。但是类似这个例子,C# 也可以通过一些函数式方法来实现。
1、可以 在 changeColor 函数里设定一个函数参数,让改变颜色后的操作写在一个函数里传入 changeColor。changeColor 在调用函数操作后再恢复原状。不过,这是另一种架构。我们不说某一种架构就比另一种架构好,但是能够按实际需求,灵活应用才是最好的。
2、写一个实现 IDisposable 接口的类,要求创建对象的时候传入一个用于恢复的函数。这种方式既然常用,写一个这样的类也不错。