將 Using Statement 重構成 Using() Function

栏目: ASP.NET · 发布时间: 5年前

内容简介:C# 有個著名的macOS High Sierra 10.13.6.NET Core 2.1

C# 有個著名的 using statement,對於實踐 IDisposable 的物件特別好用,但 using 是個 statement,在 Imperative 世界沒問題,但在 Functional 世界,statement 就類似 句點 ,讓我們無法繼續 Pipeline 或對其他 function 做 Compose,我們能否比照將 foreach statement 重構成 ForEach() function,也將 using statement 重構成 using() function 呢 ?

Version

macOS High Sierra 10.13.6

.NET Core 2.1

C# 7.2

F# 4.5

Rider 2018.1.4

C# 之 Using Statement

using System;
using System.IO;

namespace ConsoleApp
{
    public static class Program
    {
        public static void Main()
        {
            using (var streamReader = new StreamReader("TestFile.txt"))
            {
                var line = streamReader.ReadToEnd();
                Console.WriteLine(line);
                // Hello World
            }
        }
    }
}

StreamReader 是個典型實踐 IDisposable 的物件,所以在使用時都會使用 using statement 包起來,等離開 {} scope 時,自動呼叫 Dispose() 釋放 resource。

這些都是我們都習慣的 C#。

using 是 statement,在 Imperative 世界沒問題,反正程式碼都是一行一行循序執行。

但在 Functional 世界,我們要求 code 要 Pipeline,要 Compose,所以 FP 喜歡使用 expression,不喜歡 statement。

Statemet 就類似 句點 ,讓所有的 Pipeline 都中斷了。

其實仔細看 using statement,其實包含幾個部分:

  • Setup : 獲得 resource
  • Body : 執行 resource
  • Teardown : 釋放 resource

其中 using statement 就是幫我們做 teardown 部分。

因此我們可以自己寫一個 Using() function,將 setup 與 body 傳入 Using()

C# 之 Using() Function

using System.IO;
using static Functional.F;

namespace ConsoleApp
{
    public static class Program
    {
        public static void Main()
        {
            Using(new StreamReader("TestFile.txt"), ReadFile)
                .WriteLine();
                       
            string ReadFile(StreamReader streamReader) => streamReader.ReadToEnd();
        }
    }
}

10 行

Using(new StreamReader("TestFile.txt"), ReadFile)
	.WriteLine();

使用 Using() function,將 setup 傳入第一個參數,將 body 傳入第二個參數。

由於 ReadFile() 回傳為 string ,因此 Using() 也是回傳 string ,這樣就可以使用 Pipeline 方式 WriteLine() 直接印出。

13 行

string ReadFile(StreamReader streamReader) => streamReader.ReadToEnd();

Body 以 local function 定義。

至於 Using()WriteLine() 怎麼來的呢 ? 是我們自己寫的 Higher Order Function。

using System;

namespace Functional
{
    public static class F
    {
        public static R Using<TDisp, R>(TDisp disposable, Func<TDisp, R> f) where TDisp : IDisposable
        {
            using (disposable) return f(disposable);
        }

        public static void WriteLine(this string data)
        {
            Console.WriteLine(data);
        }
    }
}

第 7 行

public static R Using<TDisp, R>(TDisp disposable, Func<TDisp, R> f) where TDisp : IDisposable
{
    using (disposable) return f(disposable);
}

自己寫一個 Using() HOF,第一個參數傳入 IDisposable 物件,第二個參數傳入 body function。

12 行

public static void WriteLine(this string data)
{
    Console.WriteLine(data);
}

自己為 string 加上 WriteLine() Extension Method,這就就可以對 string 繼續 Pipeline 印出。

C# 為了讓 using 用起來更 FP,我們必須自己實作 Using()WriteLine() ,但在 Functional First 的 F#,除了提供 Imperative 的 use 外,也提供了 Functional 的 using() ,我們完全不用自己另外實作

F# 之 Use Bind

open System.IO

let readFromFile (fileName: string) = 
    use streamReader = new StreamReader(fileName)
    streamReader.ReadToEnd()
    
readFromFile "TestFile.txt"
|> printf "%A"

F# 之 use 類似於 let ,差別是 use 在離開 function 就會呼叫 Dispose() ,不需特別加上 {} 縮排一層。

由於 readFromFile() 回傳 string ,可以直接 Pipeline 接內建的 printf()

use 仍然是個 statement。

F# 之 Using() Function

open System.IO

let readFile (streamReader: StreamReader) =
    streamReader.ReadToEnd()

let readFromFile (fileName: string) =
    using(new StreamReader(fileName)) readFile 
    
readFromFile "TestFile.txt"
|> printf "%A"

第 6 行

let readFromFile (fileName: string) =
    using(new StreamReader(fileName)) readFile

改用 F# 內建的 using() ,第一個參數傳入 IDisposable 物件,第二個參數傳入 body function,其實跟自己用 C# 實作的 Using() 是一樣的。

第 3 行

let readFile (streamReader: StreamReader) =
    streamReader.ReadToEnd()

定義 body function。

由於 using()printf() 都是 F# 內建,因此我們就不必再自己實作了

Conclusion

  • 將 C# 由 using statement 改成 using() function,乍看之下意義不大;但若去看 F# 同時提供 use statement 與 using() function 時,就可看出 F# 的用心良苦,同時支援了 Imperative 與 Functional 兩種 paradigm
  • 由於 F# 每個 function 都是 composable,因此我們就不必再自已寫 WriteLine() 了,直接 printf() 就可以 pipeline 起來

Sample Code

  • C# : 完整的範例可以在我的 GitHub 上找到
  • F# : 完整的範例可以在我的 GitHub 上找到

Reference

Enrico Buonanno, Functional Programming in C#


以上所述就是小编给大家介绍的《將 Using Statement 重構成 Using() Function》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

用户体验要素

用户体验要素

Jesse James Garrett / 范晓燕 / 机械工业出版社 / 2011-7-1 / 39.00元

《用户体验要素:以用户为中心的产品设计(原书第2版)》是AJAX之父Jesse James Garrett的经典之作。本书用简洁的语言系统化地诠释了设计、技术和商业融合是最重要的发展趋势。全书共8章,包括关于用户体验以及为什么它如此重要、认识这些要素、战略层、范围层、结构层、框架层、表现层以及要素的应用。 《用户体验要素:以用户为中心的产品设计(原书第2版)》用清晰的说明和生动的图形分析了以......一起来看看 《用户体验要素》 这本书的介绍吧!

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具