C# 7.0 本地方法
VS 2017 的 C# 7.0 中引入了本地方法,本地方法是一种语法糖,允许我们在方法内定义本地方法。更加类似于函数式语言,但是,本质上还是基于面向对象实现的。
1. 本地方法
先看一个示例:

1 using static System.Console;
2
3 namespace UseLocalFunctions
4 {
5 class Program
6 {
7 static void Main(string[] args)
8 {
9 void Add(int x, int y)
10 {
11 WriteLine($"Sum of {x} and {y}: is {x + y}");
12 }
13
14 void Multiply(int x, int y)
15 {
16 WriteLine($"Multiply of {x} and {y} is: {x * y}");
17 Add(30, 10);
18 }
19
20 Add(10, 30);
21 Multiply(40, 30);
22
23 ReadLine();
24 }
25 }
26 }

在此示例中,在 Main 方法内,嵌套定义了两个方法:Add 和 Multiply。这个方法可以在 Main 方法内被使用。这种方法被称为本地方法。英文称为:Local function.
使用 ILDasm 工具,可以看到编译之后的结果。
这两个本地方法被翻译成了两个静态的私有方法,它只能在定义的方法内被调用。
本地方法的语法定义为:
<modifiers: async | unsafe> <return-type> <method-name> <parameter-list>
方法的修饰符只有两种:async 和 unsafe,所有的本地方法都是私有的
- 如果您使用了 private 修饰,会收到 编译器的错误提示:error CS0106, "The modifier 'static' is not valid for this item."
- 如果您使用了 static,会收到编译器的错误提示:error CS0106, "The modifier 'static' is not valid for this item."
2. 带有返回类型的本地方法
本地方法也可以带有返回类型。如果类型用错的话,Visual Studio 可以给出提示。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 PrintStudentMarks(101,
6 new Subject
7 {
8 SubjectName = "Math",
9 Marks = 96
10 }, new Subject
11 {
12 SubjectName = "physics",
13 Marks = 88
14 }, new Subject
15 {
16 SubjectName = "Chem",
17 Marks = 91
18 });
19
20 ReadLine();
21 }
22
23 public static void PrintStudentMarks(int studentId, params Subject[] subjects)
24 {
25 WriteLine($"Student Id{studentId} Total Marks: {CalculateMarks()}");
26 WriteLine($"Student wise marks");
27 foreach(var subject in subjects)
28 {
29 WriteLine($"Subject Name: {subject.SubjectName}\t Marks: {subject.Marks}");
30 }
31
32 decimal CalculateMarks()
33 {
34 decimal totalMarks = 0;
35 foreach(var subject in subjects)
36 {
37 totalMarks += subject.Marks;
38 }
39
40 return totalMarks;
41 }
42 }
43
44 public class Subject
45 {
46 public string SubjectName
47 {
48 get; set;
49 }
50
51 public decimal Marks
52 {
53 get; set;
54 }
55 }
56 }

3. 使用本地方法实现递归
本地方法不需要维护调用堆栈,而递归方法需要维护调用堆栈,本地方法效率更高。下面的示例演示了两种方法的区别。
注意:该示例使用了类型 BigInteger ,需要添加对程序集 System.Numeric.dll 的引用。
代码如下。

1 class Program
2 {
3 static void Main(string[] args)
4 {
5 Stopwatch watch = new Stopwatch();
6 watch.Start();
7 BigInteger f1 = GetFactorialUsingLocal(9000);
8 watch.Stop();
9 WriteLine($"Using local function: {watch.ElapsedTicks}");
10
11 watch.Reset();
12 watch.Start();
13 BigInteger f2 = GetFactorial(9000);
14 watch.Stop();
15 WriteLine($"Using recursive function: {watch.ElapsedTicks}");
16 }
17
18 private static BigInteger GetFactorialUsingLocal(int number)
19 {
20 if (number < 0)
21 throw new ArgumentException("negative number", nameof(number));
22 else if (number == 0)
23 return 1;
24 BigInteger result = number;
25 while (number > 1)
26 {
27 Multiply(number - 1);
28 number--;
29 }
30
31 void Multiply(int x) => result *= x;
32 return result;
33 }
34
35 private static BigInteger GetFactorial(int number)
36 {
37 if (number < 0)
38 throw new ArgumentException("nagative number", nameof(number));
39 return number == 0 ? 1 : number * GetFactorial(number - 1);
40 }
41 }

在我的机器上,结果如下:
Using local function: 181770 Using recursive function: 456602
可以看到两者之间的性能差异。
此时,为了传递 result ,在生成的代码中,编译器会自动做一些额外的工作。
4. 本地方法与 Lambda 的比较
1. 性能
当创建 Lambda 的时候,将会创建一个委托,这需要内存分配,因为委托是一个对象。而本地方法则不需要,它是一个真正的方法。
另外,本地方法可以更为有效地使用本地变量,Lambda 将变量放到类中,而本地方法可以使用结构,而不使用内存分配。
这意味着调用本地方法更为节约且可能内联。
2. 本地方法可以递归
Lambda 也可以实现递归,但是代码丑陋,您需要先赋予 lambda 为 null。本地方法可以更为自然地递归。
3. 本地方法可以使用泛型
Lambda 不能使用泛型。这是因为需要赋予一个实例类型的变量。
4. 本地方法可以实现迭代器
Lambda 不能使用 yield return (以及 yield break)关键字,以实现 IEnumerable<T> 返回函数。本地方法可以。
5. 本地方法更为易读
5. 其它资源:
C# 7.0 本地方法的更多相关文章
- C# 7.0 新特性2: 本地方法
本文参考Roslyn项目中的Issue:#259. 1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法 2. C# 7.0 新特性2: 本地方法 3. C# 7.0 新特性3: 模式匹配 ...
- C# 7.0 新特性:本地方法
C# 7.0:本地方法 VS 2017 的 C# 7.0 中引入了本地方法,本地方法是一种语法糖,允许我们在方法内定义本地方法.更加类似于函数式语言,但是,本质上还是基于面向对象实现的. 1. 本地方 ...
- java本地方法如何调用其他程序函数,方法详解2
Java调用本地方法(JNI浅谈) (2006-11-27 14:55:36) 转载▼ 分类: Java类文章 本人在项目开发实践中的总结和体会 前段时间公司 ...
- java本地方法如何调用其他程序函数,方法详解
JNI是Java Native Interface的缩写,中文为JAVA本地调用.从Java 1.1 开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许J ...
- 免安装版Tomcat6.0启动方法
免安装版Tomcat6.0启动方法 1.下载Tomcat Zip压缩包,解压. 2.修改startup.bat文件: 在第一行前面加入如下两行 SET JAVA_HOME=JDK目录 SET CATA ...
- WebView js 调用Java本地方法
webView = (WebView) this.findViewById(R.id.webview); WebSettings webSettings = webView.getSettings() ...
- 从几个sample来学习JAVA堆、方法区、JAVA栈和本地方法栈
最近在看<深入理解Java虚拟机>,书中给了几个例子,比较好的说明了几种OOM(OutOfMemory)产生的过程,大部分的程序员在写程序时不会太关注Java运行时数据区域的结构: 感觉有 ...
- 网络编程 -- RPC实现原理 -- RPC -- 迭代版本V2 -- 本地方法调用 整合 Spring
网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2——RPC -- 本地方法调用 + Spring 1. 配置applicationContext.xml文件 注入 bean 及 管理 bean ...
- Java调用本地方法又是怎么一回事
JNI JNI即Java Native Interface,它能在Java层实现对本地方法的调用,一般本地的实现语言主要是C/C++,其实从虚拟机层面来看JNI挺好理解,JVM主要使用C/C++ 和少 ...
随机推荐
- [Golang学习笔记] 08 链表
链表(Linked list)是一种常见数据结构,但并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针. 由于不必须按顺序存储,链表在插入的时候可以达到O(1),比顺序表快得多,但是查 ...
- Oracle入门第六天(中)——SET运算符(交并差集)
一.概述 1.SET运算符是什么 将多个查询用 SET 操作符连接组成一个新的查询 UNION/UNION ALL——并集 INTERSECT——交集 MINUS——差集(A\B=A中去掉B中也有的元 ...
- 20155328 2016-2017-2 《Java程序设计》 第一周学习总结
20155328 2016-2017-2 <Java程序设计> 第一周学习总结 教材学习内容总结 本周学习目标是浏览<Java学习笔记>中的十八章,其中第一章和第二章认真学习, ...
- Luogu1801_黑匣子_KEY
题目传送门 借这道题练一下Treap和Splay的板子. code: #include <cstdio> #include <cstdlib> using namespace ...
- C++ OI图论 学习笔记(初步完结)
矩阵图 使用矩阵图来存储有向图和无向图的信息,用无穷大表示两点之间不连通,用两点之间的距离来表示连通.无向图的矩阵图是关于主对角线对称的. 如图所示: 使用dfs和bfs对矩阵图进行遍历 多源最短路径 ...
- 【JUC源码解析】Semaphore
简介 Semaphore(信号量),概念上讲,一个信号量持有一组许可(permits). 概述 线程可调用它的acquire()方法获取一个许可,不成功则阻塞:调用release()方法来归还一个许可 ...
- python实现socket通信
python实现socket很简单,保证你的环境有响应的python环境就可以,我使用的是socket,demo代码如下: server端程序: # coding:utf-8 import socke ...
- java阻塞队列之ArrayBlockingQueue
在Java的java.util.concurrent包中定义了和多线程并发相关的操作,有许多好用的工具类,今天就来看下阻塞队列.阻塞队列很好的解决了多线程中数据的安全传输问题,其中最典型的例子就是客园 ...
- EOJ3650 转机折扣(26进制,字符串)
题面 看成26进制,把较小的那个字符串加1 strcmp(s1,s2)s1和s2有大小时,不一定都是返回1或者-1.....这个地方wa了好几次没有发现 #include<bits/stdc++ ...
- 【Linux 运维】Linux 目录
目录 [Linux 运维]Centos7初始化网络配置 [Linux 运维]linux系统修改主机名 [Linux 运维]linux系统关机.重启.注销命令 [Linux 运维]linux系统查看版本 ...