將 Using Statement 重構成 Using() Function

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

内容简介: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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

现代操作系统(原书第4版)

现代操作系统(原书第4版)

[荷] Andrew S. Tanenbaum、[荷] Herbert Bos / 陈向群、马洪兵 等 / 机械工业出版社 / 2017-7 / 89.00

Andrew S. Tanenbaum教授编写的教材《现代操作系统》现在已经是第4版了。第4版在保持原有特色的基础上,又增添了许多新的内容,反映了当代操作系统的发展与动向,并不断地与时俱进。 对比第3版,第4版有很多变化。一些是教材中多处可见的细微变化,一些是就某一功能或机制增加了对最新技术的介绍,如增加了futex同步原语、读–复制–更新(Read-Copy-Update)机制以及6级RA......一起来看看 《现代操作系统(原书第4版)》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

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

Markdown 在线编辑器

html转js在线工具
html转js在线工具

html转js在线工具