簡體   English   中英

C#擴展方法和F#管道轉發操作符之間有什么關系?

[英]What is the relation between C# extension methods and the F# pipe-forward operator?

在使用F#解決一些小問題之后,我發現將C#擴展方法看作是一種轉變方式對我自己很有幫助。 進入管道運營商'。

例如,給定一個名為int的Int32s序列,C#代碼:

ints.Where(i => i > 0)
    .Select(i => i * i)

類似於F#代碼

let where = Seq.filter
let select = Seq.map

ints |> where (fun i -> i > 0)
     |> select (fun i -> i * i)

事實上,我經常認為IEnumerable上的擴展方法只是一個函數庫,它提供與F#的Seq模塊類似的功能。

顯然,管道參數是F#函數中的最后一個參數,但是C#擴展方法中的第一個參數 - 但除此之外,在描述擴展方法或管道轉發給其他開發人員時是否存在使用該解釋的問題?

我會誤導他們,還是一個有用的類比?

我不會進行類比,因為可以調用擴展方法,就好像它是一個實例方法,而管道在語法上顯然是完全不同的。 另外,F#還有擴展方法(以及擴展屬性和事件!),所以我認為真的有可能引起混淆。

但是,我認為管道樣式和擴展方法都可以將計算流程描述為一組步驟,這可能與您正在推動的相似性。 從概念上講,如果代碼反映了“采用這組內容,只保留滿足i> 0的子集,然后將每個中的每一個都對齊”的想法,這很好,這就是用英語描述任務的方式。 管道語法和C#擴展方法都允許這種事情。

我也認為這是一個非常有用的比喻。 實際上,在我的“ 真實世界功能編程”一書中描述流水線操作符時,我使用了這個類比(它試圖向具有C#背景的人解釋功能性想法)。 以下是第6章的引用。

關於兩者之間的差異 - 存在一些概念上的差異(例如擴展方法“將成員添加到對象”),但我認為這對我們在實踐中使用它們的方式沒有任何影響。

  • C#擴展方法和F#函數之間的一個值得注意的實際區別是編輯器支持 - 當您鍵入“。”時。 在C#中,您可以看到特定類型的擴展方法。 我相信當您鍵入|> (原則上)時,F#IntelliSense可以顯示已過濾的列表,但它可能更多的工作,並且它還不支持。

  • 這兩個構造都用於啟用基於表達式的組合編程風格。 通過這個我的意思是你可以編寫更大的代碼部分作為描述應該做什么的單個表達式(沒有擴展方法/流水線操作符,你可能會將代碼分成多個步驟)。 我認為這種編程風格通常會導致更具說明性的代碼。


流水線操作符( |> )允許我們在左側寫入函數的第一個參數; 也就是說,在函數名稱之前。 如果我們想要按順序在某個值上調用多個處理函數並且我們想要先寫入正在處理的值,這非常有用。 讓我們看一個例子,展示如何在F#中反轉列表然后獲取它的第一個元素:

 List.hd(List.rev [1 .. 5]) 

這不是很優雅,因為操作以相反的順序寫入然后執行它們並且正在處理的值在右側,由幾個括號圍繞。 在C#中使用擴展方法,我們寫道:

 list.Reverse().Head(); 

在F#中,我們可以通過使用流水線操作符獲得相同的結果:

 [1 .. 5] |> List.rev |> List.hd 

即使這看起來很棘手,但操作員實際上非常簡單。 它有兩個參數 - 第二個(在右側)是一個函數,第一個(在左側)是一個值。 運算符將值作為參數提供給函數並返回結果。

在某些意義上,流水線操作類似於在對象上使用點符號調用方法,但它不限於對象的內部方法。 這與擴展方法類似,因此當我們編寫通常與流水線操作符一起使用的F#函數的C#替代時,我們將其實現為擴展方法。

如果您希望重用這些管道,您可能還需要查看反向函數組合運算符。

let where = Seq.filter
let select = Seq.map
let whereGreaterThanOneComputeSquare = 
   where (fun i -> i > 0) << select (fun i -> i * i)
ints |> whereGreaterThanOneComputeSquare

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM