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

F# 项目

在之前的几篇文章介绍的代码都在交互窗口(fsi.exe)里运行,但平常开发的软件程序可能含有大类类型和函数定义,代码不可能都在一个文件里。下面我们来看VS里提供的F#项目模板。

F#项目模板有以下几种类型(以VS2015为例): 

  • Silverlight库创建Silverlight的类库

  • 教程模板是一个控制台应用程序,里面包含了F#的示例,可通过这个项目快速了解F#相关内容。

  • “可移植库”则可创建用于多平台的库,支持的平台在括号里说明。

  • ”用于创建类库

  • 控制台应用程序”大家就熟悉了。

  • 安卓项目为安装了Xamarin创建的,请忽略。

我们创建一个控制台应用程序来说明,下图为程序的Program.fs文件及运行结果:

我们添加一行代码(图中蓝框中)防止运行结束自动退出,这个应用程序默认是把参数打印出来,而运行时参数为空,所以结果为一空数组([||])。

其中ignore函数用于丢弃System.Console.ReadKey()结果

现在项目中除了AssemblyInfo.fs外,只有Program.fs一个文件,下面我们先了解模块的相关信息再创建其他文件。

模块

模块简介

模块(Module)是F#程序代码的基本组织单位。默认情况下,每个F#代码文件(后缀为.fs)对应一个模块,且必须在文件开头指定模块名称。

创建模块

我们创建File1.fs文件时,默认会在开头添加module File1,当然也可自己改成其他名称。

module File1let x = 1

在其他模块中使用File1.x进行访问。

文件顺序

F#项目中的文件是有顺序要求的,在上面的文件无法访问下面的模块。我们可以使用Alt+上/下箭头进行调整文件顺序,或在文件上点击右键进行操作: 

嵌套模块

模块中可嵌套模块,但定义内层模块需要在模块名后使用等号(=),且内层模块的内容必须比它的上层模块缩进一级。

module TopLevelModel        module NestedModule =   //第一层嵌套模块let i = 1module NestedModuleInNestedModule =  //第二层嵌套模块let i = 2

使用模块

若想不使用模块名访问模块中的值时,则可使用open关键字进行打开。但有两个需要注意的地方:

强制显示访问

在上一章介绍的集合模块中,我们从未使用open List或者open Seq这样的操作。

使用F12转到Seq的代码定义文件可以发现Seq模块使用了
[<RequireQualifiedAccess>](强制显示访问)

附加了此特性的模块在使用时必须使用模块名访问,因为几个集合模块中有大部分函数名称是相同的,若设置此特性而可同时打开了多个模块,则函数名称将会冲突。

自动打开

而我们在使用printfnignore函数时,均不需要打开相关模块,是因为在他们所属模块附加了[<AutoOpen>](自动打开)的特性。像Operators模块里有我们常用的运算符,为了方便使用,添加了自动打开的特性。

我们在自定义模块时可根据需要使用这两个特性。

命名空间

命名空间(Namespace)和模块类似,不同的是命名空间里不能直接定义值,只能定义类型。(与C#中的命名空间一样,可以想象我们无法在C#的命名空间中直接定义一个方法,而需要首先定义一个类。)

但F#中的命名空间不能像模块那样嵌套,但可以在同一文件中定义多个命名空间。

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

上面的代码在一个文件中使用两个命名空间分别定义了一个类型。

其中Suit可区分联合(Discriminated Union)类型;PokerPlayer记录(Record)类型。将在下一篇介绍。

应用程序入口

在F#中,程序从程序集的最后一个文件开始执行,而且必须是一个模块。但最后一个模块的名称可省略

也可以使用[<EntryPoint>]特性应用于最后一个代码文件的最后一个函数,使其成为程序入口点而无需显示调用。

可查看控制台应用程序项目的模板:

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

main函数的参数是一个数组(通常可自定义为字符串数组),是应用程序的运行参数,返回的整数则为程序的退出代码(exit code)。

若不使用[<EntryPoint>],则需要在最后调用该函数,否则并不会自动调用该函数。

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

控制台应用程序通常在结束之前使用System.Console.ReadKey()方法来防止运行完成自动退出。

扩展模块

可以通过创建一个同名模块,在其中添加值来对原有模块进行扩展。

在介绍常用函数时,我们提到Seq模块没有提供rev函数,现在自己实现以Seq模块进行扩展

open System.Collections.Genericmodule Seq =    /// 反转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#互相调用

F#代码和C#代码(包括VB.NET)一样,都编译成MSIL,在CLR运行。(可参考文章《.NET框架》)所以,两种语言之间可以方便地互相调用。

程序集的引用大家都熟悉,但C#和F#中又有一些独立的东西不能互相使用,下面简单介绍一下在互相调用中常见的问题。

F#调用C#代码

本节涉及操作需要创建两个项目,一个C#的类库项目,一个F#的控制台项目。然后F#项目引用C#项目。

dynamic:在F#中访问C#的动态类型

在.NET4.0,C#引入了dynamic关键字使得可以像使用动态语言一样来使用C#。但在F#中并不支持dynamic关键字和动态类型,在引用C#编译的程序集时,则变成了Object类型。

我们知道dynamicMicrosoft.CSharp.dll程序集中实现,在F#中可以通过引用此程序集,通过反射等操作自己实现对动态类型及属性的访问。

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

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

在F#中调用:

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

打开FSharp.Interop.Dynamic命名空间,F#中可使用?来访问动态类型的属性和方法。

调用带有 ref 和 out 参数的函数

在C#中,有refout两个关键字来修饰函数的参数,使函数可以进行引用传递和返回多个值。若要在F#中调用,则有一些不同。

带有ref参数或者out参数的函数,因为参数值可能在函数中发生改变,需要在F#先定义一个可变值类型,并使用寻址操作符(&进行传入。

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

在F#中调用:

// F#代码,位于F#项目的Program.fsopen CSharpForFSharplet mutable x,y = 0,0CSharpClass.OutRefParams(&x,&y) 

返回true并对xy进行了改变。

带有out的参数在C#中可以使用未赋值的变量传入,所以在F#中除了寻址传入的方法,还可以直接忽略该参数,则该函数在F#中成为了多返回值(即返回tuple)的形式:

let successful, result = Int32.TryParse(str)

Int32.TryParse返回了两个值,第一个总是函数返回值,而后是out参数。

柯里化C#的方法

因为C#中的函数无论有多少个参数,在F#中调用时都视为一个tuple参数,所以无法柯里化和使用函数管道符(|>)操作。

在F#中可以使用FuncConvert类将.NET中的函数转换成F#中的函数。

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转化为F#中的函数,因为该方法具有多个重载,所以第一行代码用来指定一个要转换的重载。

其实FuncConvert类也可以在C#中使用,需要添加FSharp.Core程序集,有兴趣的可以自己尝试。

C#调用F#代码

本节涉及操作需要创建两个项目,一个F#的类库项目,一个C#的控制台项目。然后C#项目引用F#项目,因为涉及到F#中独有类型,还需要引用FSharp.Core程序集。

若要在UWP项目中引用F#项目,需要通过“可移植库”模板创建项目。

因为C#中的类型比F#少了很多,所以很多C#不支持的类型均使用来代替,使用时只需像使用类一样使用它就行了。而模块,在C#中则为静态类

F#中的函数

需要注意的是,若在F#将函数作为参数或返回值,则F#中的函数在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函数返回一个将三个字符串转成int再相加的函数,在C#中调用此函数:

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

F#中的string -> string -> string -> int类型函数在C#中变成了FSharpFunc <string, FSharpFunc <string, FSharpFunc <string, int>>>

这是因为C#中的不支持函数柯里化,如果F#中的函数需要更多的参数,在C#中调用就很麻烦了。虽然在F#使用很方便,但若需要编写供C#使用的程序集,尽量不要使用这些功能。

命名规范

通过上面的了解,至少可以简单地使用F#和C#互相调用。但有个地方可能使有强迫症的程序员很难受:F#模块中的函数命名使用的是驼峰式(camelCase),在C#中类的方法则使用PascalCase命名规范。

F#模块在编译成静态类后,在C#中使用变得不一致。在F#中提供了CompiledName特性用来指定编译后的名称

在第一篇中提到的F#中可用“`` ``”来使任何字符串作为变量(值)的名称,若想在C#中调用这类值(不符合变量命名规则),也需要用CompiledName指定编译后的名称,否则无法调用。

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

在C#中调用:

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

相关文章:

  • 如果你也会C#,那不妨了解下F#(1):F# 数据类型

  • 如果你也会C#,那不妨了解下F#(2):数值运算和流程控制语法

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

  • 如果你也会C#,那不妨了解下F#(4):了解函数及常用函数

  • 【送书活动】机器学习项目开发实战

  • 《机器学习项目开发实战》送书活动结果公布

  • F#年度调查结果概述

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


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328565.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

用户模块开发 分类模块 商品模块 购物车模块

分类表 https://openhome.alipay.com/platform/appDaily.htm?tabaccount 沙箱 沙箱环境使用说明 https://docs.open.alipay.com/200/105311 https://docs.open.alipay.com/200/105311 当面付 app都有文档在最下面 https://docs.open.alipay.com/204/105297 app集成文…

hbase 单机连接hadoop_Hadoop、Hbase单机环境安装

1. Hadoop安装1.1 HDFS配置fs.defaultFShdfs://localhost:9000hadoop.tmp.dirfile:/home/local/data/hadoop/tmpdfs.replication1dfs.namenode.name.dirfile:/home/local/data/hadoop/tmp/dfs/namedfs.datanode.data.dirfile:/home/local/data/hadoop/tmp/dfs/data编辑Hadoop下…

高效的SQLSERVER分页查询

Sqlserver数据库分页查询一直是Sqlserver的短板&#xff0c;闲来无事&#xff0c;想出几种方法&#xff0c;假设有表ARTICLE,字段ID、YEAR...(其他省略)&#xff0c;数据53210条(客户真实数据&#xff0c;量不大)&#xff0c;分页查询每页30条&#xff0c;查询第1500页&#xf…

Java中classLoader浅析

转载自 Java中classLoader浅析本文为在公司内部TD上写的一篇小文, 主要讲解java中classLoader基础知识, 现在拿来这里分享一下. 一、问题 请在Eclipse中新建如下类&#xff0c;并运行它&#xff1a; package java.lang;public class Long {public static void main(String[] …

微软BUG Bounty悬赏项目扩展至.NET Core和ASP.NET Core

微软宣布自 2016 年 9 月 1 日开始将 .NET Core 和 ASP.NET 纳入到 BUG Bounty 悬赏项目范围内&#xff0c;微软将会为 Windows 和 Linux 平台上的两个编程代码提供漏洞悬赏。 微软公布的细节中写道&#xff1a; 微软将会为、最新微软 .NET Core 和 ASP.NET Core 的最新 RTM 版…

计算字典的个数_[LeetCode] 440. 字典序的第K小数字

题目链接&#xff1a; https://leetcode-cn.com/problems/k-th-smallest-in-lexicographical-order难度&#xff1a;困难通过率&#xff1a;28.4%题目描述:给定整数 n 和 k&#xff0c;找到 1 到 n 中字典序第 k 小的数字。注意&#xff1a;1 ≤ k ≤ n ≤ 109。示例:**输入:**…

ASP.NET Core开发-Docker部署运行

ASP.NET Core开发Docker部署&#xff0c;.NET Core支持Docker 部署运行。我们将ASP.NET Core 部署在Docker 上运行。 大家可能都见识过Docker &#xff0c;今天我们就详细了解一下Docker的用途&#xff0c;以及真实的应用场景。 Docker源于PaaS&#xff0c;PaaS的应用场景即是D…

java异常捕获的一点感悟

转载自 java异常捕获的一点感悟 class Annoyance extends Exception {} class Sneeze extends Annoyance {} class Human { public static void main(String[] args) throws Exception { try { try { throw new Sneeze(); } catch ( Annoyance a ) { System.out…

android修改机型cpu,mac,androidid....

https://blog.csdn.net/yyy_bbb_lll/article/details/80734881 android 改机&#xff0c;抹机工具开发 一 2018年06月19日 16:43:40 阅读数&#xff1a;865 本工具以Xposed框架为基础&#xff0c;实现了改机软件所需的大部分功能。先贴界面图&#xff1a; 设备基本信息的显示…

data layui table 排序_浅谈layui中table的sort排序

table模块是layui框架最核心的组成之一&#xff0c;它用于对表格进行一些列功能和动态化数据操作&#xff0c;本文介绍了layui中table的sort排序&#xff0c;解决了在我们使用sort排序时可能遇到的一些问题。今天来谈谈table sort的那点事。预告一下&#xff0c;目的是做到前台…

gRPC C#学习

前些天gRPC 发布1.0 版本&#xff0c;代表着gRPC 已经正式进入稳定阶段。 今天我们就来学习gRPC C# 。而且目前也已经支持.NET Core 可以实现完美跨平台。 传统的.NET 可以通过Mono 来实现跨平台调用。 GitHub&#xff1a; https://github.com/grpc/grpc gRPC 简单介绍&#x…

Java日期及时间库插件 -- Joda Time.

转载自 Java日期及时间库插件 -- Joda Time.来到新公司工作也有一个多月了, 陆陆续续做了一些简单的项目. 今天做一个新东西的时候发现了 Joda Time的这个东西, 因为以前用的都是JDK原生的时间处理API, 大家都知道Java原生的时间处理的API一直都是不太好用, 所以这个有必要去…

电商校招指导

https://www.imooc.com/article/19998 https://www.imooc.com/article/18998 鼓励大家多总结 有道笔记可以记录下来 多年总结的 平时要多总结 https://www.imooc.com/article/19094 温馨tips&#xff1a; 校招的要求会比社招低很多&#xff0c;大家要把握好机会。因为校招…

大咖开讲:一小时学会.NET MVC开发的那些事儿

许多ASP.NET开发人员开始接触MVC认为MVC与ASP.NET完全没有关系&#xff0c;是一个全新的Web开发&#xff0c;事实上ASP.NET是创建WEB应用的框架&#xff0c;而MVC是能够用更好的方法来组织并管理代码的一种更高级架构体系。我们可将原来的ASP.NET称为ASP.NET Webform&#xff0…

HashMap的实现原理及其特点

HashMap的实现原理及其特点 2018年03月15日 20:43:08 阅读数&#xff1a;11045更多 个人分类&#xff1a; Java基础知识点 版权声明&#xff1a;本文为博主原创文章&#xff0c;未经博主允许不得转载。 https://blog.csdn.net/lovewebeye/article/details/79573702 1) Hash…

重写equals就必须重写hashCode的原理分析

转载自 重写equals就必须重写hashCode的原理分析因为最近在整理Java集合的源码&#xff0c; 所以今天再来谈谈这个古老的话题&#xff0c;因为后面讲HashMap会用到这个知识点&#xff0c; 所以重新梳理下。如果不被重写&#xff08;原生Object&#xff09;的hashCode和equals是…

mysql unique count_MySQL - Count Number of Unique Values

问题If I have three columns:orderNumber, name, emailand I would like to count how many unique emails are in the table how would I go about doing so?A statement like:SELECT count(email) FROM ordersgives me the total count.I tried SELECT DISTINCT count(emai…

如何在 ASP.NET MVC 中集成 AngularJS(2)

在如何在 ASP.NET MVC 中集成 AngularJS(1)中&#xff0c;我们介绍了 ASP.NET MVC 捆绑和压缩、应用程序版本自动刷新和工程构建等内容。 下面介绍如何在 ASP.NET MVC 中集成 AngularJS 的第二部分。 ASP.NET 捆绑和压缩 CSS 和 JavaScript 的捆绑与压缩功能是 ASP.NET MVC 最流…

bean交个spring和new比较区别

在spring的配置文件中我们要使用DataSource这个对吧 这个bean将会被多个bean引用 通过ref"dateSource"这个引用 当我们要修改这个只要修改引用就好了 不需要修改很多了 主要是解耦 比如你有一个A类 在好几个类里边要调用到A的方法 new的话就要在每个类里都new…

什么是中间件

转载自 什么是中间件什么是中间件&#xff1f; 中间件就是程序中可织入的&#xff0c;可重用的&#xff0c;与业务逻辑无关的各种组件。中间件&#xff08; middleware &#xff09;是基础软件的一大类&#xff0c;属于可复用软件的范畴。顾名思义&#xff0c;中 间件处于操作…