將 Using Statement 重構成 Using() Function

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

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

查看所有标签

猜你喜欢:

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

Essential PHP Security

Essential PHP Security

Chris Shiflett / O'Reilly Media / 2005-10-13 / USD 29.95

Being highly flexible in building dynamic, database-driven web applications makes the PHP programming language one of the most popular web development tools in use today. It also works beautifully wit......一起来看看 《Essential PHP Security》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具