iT邦幫忙

2022 iThome 鐵人賽

DAY 7
0

Closure(閉包)是一種特殊的函數,可以讀取外面環境中的資料,而外部無法修改函數內部的資料。這樣的介紹有如天書,實際上Closure是一個C#工程師幾乎天天在寫的東西,再次把從資料庫取得會員資料的程式搬出來:

public Person GetPersonById(long id)
{
		return _dbContext.Person.FirstOrDefault(x => x.Id == id);
}

1. Lambda Expression

仔細觀察FirstOrDefault裡面的lambda expression,把它擷取出來:

x => x.Id == id;

由於lambda其實就是一個方法,這個方法僅具有一個引數x,但對它來說id是一個變數,既然沒有將id傳入方法,那方法本身又是怎麼知道的呢?先回到GetPersonById,我們知道id的作用範圍在這個方法裡面,那麼對於lambda來說,它就是直接取得了這個作用範圍內的環境變數,這個結構就是所謂的closure。

我們再來看昨天寫的有點微妙的委派,為了容易解釋closure把它用另一個方式寫出來

Func<long, Func<string, Func<DateTime, bool>>> currying1 = 
        id => name => date => ApplyInsurance(id,name,date);

// 可以寫成
Func<long, Func<string, Func<DateTime, bool>>> insurance2 =
    (long id) =>
    {
        return (string name) =>
        {
            return (DateTime date) =>
            {
                return ApplyInsurance(id, name, date);
            };
        };
    };

下面這個一層包一層的作法,對於最內層的lambda來說取得了外面的id與name變數,其實就是一個多層closure的形式。

2. Local Function

除了lambda expression以外,C#中還有一個東西會用到閉包叫做區域函式,比起lambda來說大家可能比較不習慣使用,但是熟悉後會覺得非常好用,假設我今天有個方法希望呼叫某個外部api後把response整理並且存到database,並且回傳操作是否成功

public bool GetInfomationAndSave(Request entity)
{
		var apiKey = GetApiKey();

		return GetApiResult(entity)
								               .Bind(Save)
							                 .Bind(IsSuccess);
		
		Either<Fail,Response> GetApiResult(Request entity) => 
		{
					var client = new HttpClient();
					client.SetKey(apiKey);  // 取得環境變數 
					// ...剩下的實做
		}
		
		Either<Fail,int> Save(Response) => // ...

		bool IsSuccess(Either<Fail,int> result) => // ...
}

這邊有稍微展示了函數式設計的撰寫風格,C#中可以將各個步驟拆成local function,再透過HOF把每個步驟串起來組成方法鏈,而apiKey可以透過Closure傳遞到區域函式中。Either是FP中對於例外處裡的抽象,預計之後再跟錯誤處裡一起介紹。

補充一下我很喜歡寫local function,在設計程式的時候為了關注點分離,我們會將某些步驟抽成private方法,但很多時候抽出來的方法其實沒有重用性,從這個角度來思考又會覺得抽private有點多餘,我覺得local function很適合這個用途。順帶一提.net6的Top-level statements,在寫Console專案的時候已經看不到main()了,在裡面定義的方法會是local function。

小結

Closure是讓函數取得外部變數的技巧,在FP中我們會將函數作為物件傳遞,這是一個幫助建構函數內容的技巧,明天預計先找個實例來小試一下函數式的設計風格。


上一篇
Day6. Currying
下一篇
Day8. 試試看用函數思考
系列文
Functional Programming with C#30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言