日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > C# >内容正文

C#

如果你也会C#,那不妨了解下F#(5):模块、与C#互相调用

發(fā)布時間:2023/12/4 C# 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 如果你也会C#,那不妨了解下F#(5):模块、与C#互相调用 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

F# 項(xiàng)目

在之前的幾篇文章介紹的代碼都在交互窗口(fsi.exe)里運(yùn)行,但平常開發(fā)的軟件程序可能含有大類類型和函數(shù)定義,代碼不可能都在一個文件里。下面我們來看VS里提供的F#項(xiàng)目模板。

F#項(xiàng)目模板有以下幾種類型(以VS2015為例):?

  • Silverlight庫創(chuàng)建Silverlight的類庫

  • 教程模板是一個控制臺應(yīng)用程序,里面包含了F#的示例,可通過這個項(xiàng)目快速了解F#相關(guān)內(nèi)容。

  • “可移植庫”則可創(chuàng)建用于多平臺的庫,支持的平臺在括號里說明。

  • ”用于創(chuàng)建類庫

  • 控制臺應(yīng)用程序”大家就熟悉了。

  • 安卓項(xiàng)目為安裝了Xamarin創(chuàng)建的,請忽略。

我們創(chuàng)建一個控制臺應(yīng)用程序來說明,下圖為程序的Program.fs文件及運(yùn)行結(jié)果:

我們添加一行代碼(圖中藍(lán)框中)防止運(yùn)行結(jié)束自動退出,這個應(yīng)用程序默認(rèn)是把參數(shù)打印出來,而運(yùn)行時參數(shù)為空,所以結(jié)果為一空數(shù)組([||])。

其中ignore函數(shù)用于丟棄System.Console.ReadKey()結(jié)果

現(xiàn)在項(xiàng)目中除了AssemblyInfo.fs外,只有Program.fs一個文件,下面我們先了解模塊的相關(guān)信息再創(chuàng)建其他文件。

模塊

模塊簡介

模塊(Module)是F#程序代碼的基本組織單位。默認(rèn)情況下,每個F#代碼文件(后綴為.fs)對應(yīng)一個模塊,且必須在文件開頭指定模塊名稱。

創(chuàng)建模塊

我們創(chuàng)建File1.fs文件時,默認(rèn)會在開頭添加module?File1,當(dāng)然也可自己改成其他名稱。

module File1let x = 1

在其他模塊中使用File1.x進(jìn)行訪問。

文件順序

F#項(xiàng)目中的文件是有順序要求的,在上面的文件無法訪問下面的模塊。我們可以使用Alt+上/下箭頭進(jìn)行調(diào)整文件順序,或在文件上點(diǎn)擊右鍵進(jìn)行操作:?

嵌套模塊

模塊中可嵌套模塊,但定義內(nèi)層模塊需要在模塊名后使用等號(=),且內(nèi)層模塊的內(nèi)容必須比它的上層模塊縮進(jìn)一級。

module TopLevelModel ? ? ? ?module NestedModule = ? //第一層嵌套模塊let i = 1module NestedModuleInNestedModule = ?//第二層嵌套模塊let i = 2

使用模塊

若想不使用模塊名訪問模塊中的值時,則可使用open關(guān)鍵字進(jìn)行打開。但有兩個需要注意的地方:

強(qiáng)制顯示訪問

在上一章介紹的集合模塊中,我們從未使用open List或者open Seq這樣的操作。

使用F12轉(zhuǎn)到Seq的代碼定義文件可以發(fā)現(xiàn)Seq模塊使用了
[<RequireQualifiedAccess>](強(qiáng)制顯示訪問)

附加了此特性的模塊在使用時必須使用模塊名訪問,因?yàn)閹讉€集合模塊中有大部分函數(shù)名稱是相同的,若設(shè)置此特性而可同時打開了多個模塊,則函數(shù)名稱將會沖突。

自動打開

而我們在使用printfn和ignore函數(shù)時,均不需要打開相關(guān)模塊,是因?yàn)樵谒麄兯鶎倌K附加了[<AutoOpen>](自動打開)的特性。像Operators模塊里有我們常用的運(yùn)算符,為了方便使用,添加了自動打開的特性。

我們在自定義模塊時可根據(jù)需要使用這兩個特性。

命名空間

命名空間(Namespace)和模塊類似,不同的是命名空間里不能直接定義值,只能定義類型。(與C#中的命名空間一樣,可以想象我們無法在C#的命名空間中直接定義一個方法,而需要首先定義一個類。)

但F#中的命名空間不能像模塊那樣嵌套,但可以在同一文件中定義多個命名空間。

namespace PlayingCardstype Suit = Spade | Club | Diamond | Heartnamespace PlayingCards.Pokertype PokerPlayer = {Name:string; Money:int; Position:int}

上面的代碼在一個文件中使用兩個命名空間分別定義了一個類型。

其中Suit為可區(qū)分聯(lián)合(Discriminated Union)類型;PokerPlayer為記錄(Record)類型。將在下一篇介紹。

應(yīng)用程序入口

在F#中,程序從程序集的最后一個文件開始執(zhí)行,而且必須是一個模塊。但最后一個模塊的名稱可省略

也可以使用[<EntryPoint>]特性應(yīng)用于最后一個代碼文件的最后一個函數(shù),使其成為程序入口點(diǎn)而無需顯示調(diào)用。

可查看控制臺應(yīng)用程序項(xiàng)目的模板:

[<EntryPoint>]let main argv = ? ? printfn "%A" argv ? ?0

main函數(shù)的參數(shù)是一個數(shù)組(通常可自定義為字符串?dāng)?shù)組),是應(yīng)用程序的運(yùn)行參數(shù),返回的整數(shù)則為程序的退出代碼(exit code)。

若不使用[<EntryPoint>],則需要在最后調(diào)用該函數(shù),否則并不會自動調(diào)用該函數(shù)。

let main (argv:string[]) = printfn "%A" argvSystem.Console.ReadKey(true) |> ignore ? ?0main [||]

控制臺應(yīng)用程序通常在結(jié)束之前使用System.Console.ReadKey()方法來防止運(yùn)行完成自動退出。

擴(kuò)展模塊

可以通過創(chuàng)建一個同名模塊,在其中添加值來對原有模塊進(jìn)行擴(kuò)展。

在介紹常用函數(shù)時,我們提到Seq模塊沒有提供rev函數(shù),現(xiàn)在自己實(shí)現(xiàn)以對Seq模塊進(jìn)行擴(kuò)展

open System.Collections.Genericmodule Seq = ? ?/// 反轉(zhuǎn)Seq中的元素let rec rev (s : seq<'a>) = ? ? ? ?let stack = new Stack<'a>()s |> Seq.iter stack.Pushseq { ? ? ? ? ? ?while stack.Count > 0 doyield stack.Pop()}

其中使用了.NET框架中的泛型集合類型(System.Collections.Generic.Stack<T>)。

與C#互相調(diào)用

F#代碼和C#代碼(包括VB.NET)一樣,都編譯成MSIL,在CLR運(yùn)行。(可參考文章《.NET框架》)所以,兩種語言之間可以方便地互相調(diào)用。

程序集的引用大家都熟悉,但C#和F#中又有一些獨(dú)立的東西不能互相使用,下面簡單介紹一下在互相調(diào)用中常見的問題。

F#調(diào)用C#代碼

本節(jié)涉及操作需要創(chuàng)建兩個項(xiàng)目,一個C#的類庫項(xiàng)目,一個F#的控制臺項(xiàng)目。然后F#項(xiàng)目引用C#項(xiàng)目。

dynamic:在F#中訪問C#的動態(tài)類型

在.NET4.0,C#引入了dynamic關(guān)鍵字使得可以像使用動態(tài)語言一樣來使用C#。但在F#中并不支持dynamic關(guān)鍵字和動態(tài)類型,在引用C#編譯的程序集時,則變成了Object類型。

我們知道dynamic在Microsoft.CSharp.dll程序集中實(shí)現(xiàn),在F#中可以通過引用此程序集,通過反射等操作自己實(shí)現(xiàn)對動態(tài)類型及屬性的訪問。

而我在平常一般使用第三方庫FSharp.Interop.Dynamic(Nuget)。代碼示例:

//C#代碼,命名空間CSharpForFSharppublic class CSharpClass{ ?public dynamic TestDynamic() ?{ ? ?return "5566";} }

在F#中調(diào)用:

//F#代碼,位于F#項(xiàng)目的Program.fsopen FSharp.Interop.Dynamicopen CSharpForFSharp ? ? ? ? ? ?//C#項(xiàng)目中的命名空間[<EntryPoint>]let main argv = ? ? let cc = CSharpClass() ? ?let str = cc.TestDynamic() ? ?printfn "%A" (str?Length) ? //使用?替代.System.Console.Read()|>ignore ? ?0

打開FSharp.Interop.Dynamic命名空間,F#中可使用?來訪問動態(tài)類型的屬性和方法。

調(diào)用帶有?ref?和?out?參數(shù)的函數(shù)

在C#中,有ref和out兩個關(guān)鍵字來修飾函數(shù)的參數(shù),使函數(shù)可以進(jìn)行引用傳遞和返回多個值。若要在F#中調(diào)用,則有一些不同。

帶有ref參數(shù)或者out參數(shù)的函數(shù),因?yàn)閰?shù)值可能在函數(shù)中發(fā)生改變,需要在F#先定義一個可變值類型,并使用尋址操作符(&)進(jìn)行傳入。

// C#代碼,位于命名空間CSharpForFSharppublic class CSharpClass{ ? ?public static bool OutRefParams(out int x, ref int y) ? ?{x = 100;y = y * y; ? ? ? ?return true;} }

在F#中調(diào)用:

// F#代碼,位于F#項(xiàng)目的Program.fsopen CSharpForFSharplet mutable x,y = 0,0CSharpClass.OutRefParams(&x,&y)

返回true并對x和y進(jìn)行了改變。

帶有out的參數(shù)在C#中可以使用未賦值的變量傳入,所以在F#中除了尋址傳入的方法,還可以直接忽略該參數(shù),則該函數(shù)在F#中成為了多返回值(即返回tuple)的形式:

let successful, result = Int32.TryParse(str)

Int32.TryParse返回了兩個值,第一個總是函數(shù)返回值,而后是out參數(shù)。

柯里化C#的方法

因?yàn)镃#中的函數(shù)無論有多少個參數(shù),在F#中調(diào)用時都視為一個tuple參數(shù),所以無法柯里化和使用函數(shù)管道符(|>)操作。

在F#中可以使用FuncConvert類將.NET中的函數(shù)轉(zhuǎn)換成F#中的函數(shù)。

let join : string*string list -> string = System.String.Joinlet curryJoin = FuncConvert.FuncFromTupled join[ 1..10 ] |> List.map string|> curryJoin "*" ? ? ? ? ? ? ? ?// "1*2*3*4*5*6*7*8*9*10"let joinStar = curryJoin "*" ? ?// joinStar類型為:string list -> string

以上代碼將System.String.Join轉(zhuǎn)化為F#中的函數(shù),因?yàn)樵摲椒ň哂卸鄠€重載,所以第一行代碼用來指定一個要轉(zhuǎn)換的重載。

其實(shí)FuncConvert類也可以在C#中使用,需要添加FSharp.Core程序集,有興趣的可以自己嘗試。

C#調(diào)用F#代碼

本節(jié)涉及操作需要創(chuàng)建兩個項(xiàng)目,一個F#的類庫項(xiàng)目,一個C#的控制臺項(xiàng)目。然后C#項(xiàng)目引用F#項(xiàng)目,因?yàn)?strong>涉及到F#中獨(dú)有類型,還需要引用FSharp.Core程序集。

若要在UWP項(xiàng)目中引用F#項(xiàng)目,需要通過“可移植庫”模板創(chuàng)建項(xiàng)目。

因?yàn)镃#中的類型比F#少了很多,所以很多C#不支持的類型均使用來代替,使用時只需像使用類一樣使用它就行了。而模塊,在C#中則為靜態(tài)類

F#中的函數(shù)

需要注意的是,若在F#將函數(shù)作為參數(shù)或返回值,則F#中的函數(shù)在C#中將會變成

FSharpFunc<_,_>對象(位于FSharp.Core程序集的Microsoft.FSharp.Core命名空間)。

//F# 代碼,位于TestModule模塊open Systemtype MathUtilities = ? ?static member GetAdder() =(fun x y z -> Int32.Parse(x) + Int32.Parse(y) + Int32.Parse(z))

GetAdder函數(shù)返回一個將三個字符串轉(zhuǎn)成int再相加的函數(shù),在C#中調(diào)用此函數(shù):

FSharpFunc<string, FSharpFunc<string, FSharpFunc<string, int>>> ss = MathUtilities.GetAdder();var ret = ss.Invoke("123").Invoke("45").Invoke("67");

F#中的string -> string -> string -> int類型函數(shù)在C#中變成了FSharpFunc <string, FSharpFunc <string, FSharpFunc <string, int>>>。

這是因?yàn)镃#中的不支持函數(shù)柯里化,如果F#中的函數(shù)需要更多的參數(shù),在C#中調(diào)用就很麻煩了。雖然在F#使用很方便,但若需要編寫供C#使用的程序集,盡量不要使用這些功能。

命名規(guī)范

通過上面的了解,至少可以簡單地使用F#和C#互相調(diào)用。但有個地方可能使有強(qiáng)迫癥的程序員很難受:F#模塊中的函數(shù)命名使用的是駝峰式(camelCase),在C#中類的方法則使用PascalCase命名規(guī)范。

F#模塊在編譯成靜態(tài)類后,在C#中使用變得不一致。在F#中提供了CompiledName特性用來指定編譯后的名稱

在第一篇中提到的F#中可用“`` ``”來使任何字符串作為變量(值)的名稱,若想在C#中調(diào)用這類值(不符合變量命名規(guī)則),也需要用CompiledName指定編譯后的名稱,否則無法調(diào)用。

module TestModule[<CompiledName("Add")>]let add = fun a b -> a+b[<CompiledName("IsSeven")>]let ``7?`` i = i % 7 = 0

在C#中調(diào)用:

int i = TestModule.Add(3,4);var b = TestModule.IsSeven(7);

相關(guān)文章:

  • 如果你也會C#,那不妨了解下F#(1):F# 數(shù)據(jù)類型

  • 如果你也會C#,那不妨了解下F#(2):數(shù)值運(yùn)算和流程控制語法

  • 如果你也會C#,那不妨了解下F#(3):F#集合類型和其他核心類型

  • 如果你也會C#,那不妨了解下F#(4):了解函數(shù)及常用函數(shù)

  • 【送書活動】機(jī)器學(xué)習(xí)項(xiàng)目開發(fā)實(shí)戰(zhàn)

  • 《機(jī)器學(xué)習(xí)項(xiàng)目開發(fā)實(shí)戰(zhàn)》送書活動結(jié)果公布

  • F#年度調(diào)查結(jié)果概述

原文地址:http://www.cnblogs.com/hjklin/p/fs-for-cs-dev-5.html


.NET社區(qū)新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關(guān)注

總結(jié)

以上是生活随笔為你收集整理的如果你也会C#,那不妨了解下F#(5):模块、与C#互相调用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。