C# 7.0:本地方法

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

1. 本地方法

先看一个示例:

 using static System.Console;

 namespace UseLocalFunctions
{
class Program
{
static void Main(string[] args)
{
void Add(int x, int y)
{
WriteLine($"Sum of {x} and {y}: is {x + y}");
} void Multiply(int x, int y)
{
WriteLine($"Multiply of {x} and {y} is: {x * y}");
Add(, );
} Add(, );
Multiply(, ); ReadLine();
}
}
}

在此示例中,在 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 可以给出提示。

 class Program
{
static void Main(string[] args)
{
PrintStudentMarks(,
new Subject
{
SubjectName = "Math",
Marks =
}, new Subject
{
SubjectName = "physics",
Marks =
}, new Subject
{
SubjectName = "Chem",
Marks =
}); ReadLine();
} public static void PrintStudentMarks(int studentId, params Subject[] subjects)
{
WriteLine($"Student Id{studentId} Total Marks: {CalculateMarks()}");
WriteLine($"Student wise marks");
foreach(var subject in subjects)
{
WriteLine($"Subject Name: {subject.SubjectName}\t Marks: {subject.Marks}");
} decimal CalculateMarks()
{
decimal totalMarks = ;
foreach(var subject in subjects)
{
totalMarks += subject.Marks;
} return totalMarks;
}
} public class Subject
{
public string SubjectName
{
get; set;
} public decimal Marks
{
get; set;
}
}
}

3. 使用本地方法实现递归

本地方法不需要维护调用堆栈,而递归方法需要维护调用堆栈,本地方法效率更高。下面的示例演示了两种方法的区别。

注意:该示例使用了类型 BigInteger ,需要添加对程序集 System.Numeric.dll 的引用。

代码如下。

 class Program
{
static void Main(string[] args)
{
Stopwatch watch = new Stopwatch();
watch.Start();
BigInteger f1 = GetFactorialUsingLocal();
watch.Stop();
WriteLine($"Using local function: {watch.ElapsedTicks}"); watch.Reset();
watch.Start();
BigInteger f2 = GetFactorial();
watch.Stop();
WriteLine($"Using recursive function: {watch.ElapsedTicks}");
} private static BigInteger GetFactorialUsingLocal(int number)
{
if (number < )
throw new ArgumentException("negative number", nameof(number));
else if (number == )
return ;
BigInteger result = number;
while (number > )
{
Multiply(number - );
number--;
} void Multiply(int x) => result *= x;
return result;
} private static BigInteger GetFactorial(int number)
{
if (number < )
throw new ArgumentException("nagative number", nameof(number));
return number == ? : number * GetFactorial(number - );
}
}

在我的机器上,结果如下:

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 新特性:本地方法的更多相关文章

  1. C# 7.0 新特性2: 本地方法

    本文参考Roslyn项目中的Issue:#259. 1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法 2. C# 7.0 新特性2: 本地方法 3. C# 7.0 新特性3: 模式匹配 ...

  2. C# 7.0 新特性1: 基于Tuple的“多”返回值方法

    本文基于Roslyn项目中的Issue:#347 展开讨论. 1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法 2. C# 7.0 新特性2: 本地方法 3. C# 7.0 新特性3: ...

  3. C# 7.0 新特性3: 模式匹配

    本文参考Roslyn项目Issue:#206,及Docs:#patterns. 1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法 2. C# 7.0 新特性2: 本地方法 3. C# ...

  4. C# 7.0 新特性4: 返回引用

    本文参考Roslyn项目中的Issue:#118. 1. C# 7.0 新特性1: 基于Tuple的“多”返回值方法 2. C# 7.0 新特性2: 本地方法 3. C# 7.0 新特性3: 模式匹配 ...

  5. c# 6.0新特性(二)

    写在前面 上篇文章介绍了c#6.0的using static,Auto Property Initializers,Index Initializers新的特性,这篇文章将把剩下的几个学习一下. 原文 ...

  6. C#6.0,C#7.0新特性

    C#6.0新特性 Auto-Property enhancements(自动属性增强) Read-only auto-properties (真正的只读属性) Auto-Property Initia ...

  7. 【c#】6.0与7.0新特性介绍记录

    c#发展史 引用地址:https://www.cnblogs.com/ShaYeBlog/p/3661424.html 6.0新特性 1.字符串拼接优化 语法格式:$”string {参数}” 解释: ...

  8. Javaweb学习笔记——(七)——————myexlipse基本使用、jdk5.0新特性及反射讲解

    1.debug调试模式: *使用这种模式,调试程序(看到程序运行停止在这一行) -显示出来行号 -双击左边,出现一个圆点,表示设置了一个断点 *使用debug as方式,运行程序 -特使是否进入到调试 ...

  9. webpack 4.0.0-beta.0 新特性介绍

    webpack 可以看做是模块打包机.它做的事情是:分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等),并将其打包为合适的格式 ...

随机推荐

  1. 线上Java程序导致服务器CPU占用率过高的问题排除过程

    博文转至:http://www.jianshu.com/p/3667157d63bb,博文更好效果看原版,转本博文的目的就算是个书签吧,需要时候可以定位原文学习 1.故障现象 客服同事反馈平台系统运行 ...

  2. HTML中document.getElementById()方法的操作

    转自:http://blog.csdn.net/pyffcwj/article/details/7240232/ obj = document.getElementById("cc" ...

  3. Centos6.6下安装Python3.5

    centos6.6自带的Python2.6,如果想要安装新版本的Python例如Python2.7+或者Python3.5,不能够用yum安装,那么只能从源码编译安装. Step 1: 安装依赖库和编 ...

  4. Project 7:自然数的拆分

    自然数的拆分:任何一个大于1的自然数N,总可以拆分成若干个自然数之和,并且有多种拆分方法.例如自然数5,可以有如下一些拆分方法: 5=1+1+1+1+1 5=1+1+1+2 5=1+2+2 5=1+4 ...

  5. Springboot与Thymeleaf模板引擎整合基础教程(附源码)

    前言 由于在开发My Blog项目时使用了大量的技术整合,针对于部分框架的使用和整合的流程没有做详细的介绍和记录,导致有些朋友用起来有些吃力,因此打算在接下来的时间里做一些基础整合的介绍,当然,可能也 ...

  6. 【Java学习笔记之三十三】详解Java中try,catch,finally的用法及分析

    这一篇我们将会介绍java中try,catch,finally的用法 以下先给出try,catch用法: try { //需要被检测的异常代码 } catch(Exception e) { //异常处 ...

  7. NHibernate教程(21)——二级缓存(下)

    本节内容 引入 使用NHibernate二级缓存 启用缓存查询 管理NHibernate二级缓存 结语 引入 这篇我还继续上一篇的话题聊聊NHibernate二级缓存剩下的内容,比如你修改.删除数据时 ...

  8. 团队作业4——第一次项目冲刺(Alpha版本)2017.4.22

    昨天来不及编写,这是4月22日的日志,现在补上. 1.开完站立式会议后的合照 2.任务分解图 3.开会讨论的结果,任务分派 队员 今日进展 明日安排 陈鑫龙 原型设计图分析,设计登陆界面原稿 实现登陆 ...

  9. 201521123081《Java程序设计》 第4周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 参考资料:百度脑图(上图为第3周实验学习总结中未展开部分) 1.2 使用常规方法总结其他上课内容. 多态.思维导图中有提及. 2. ...

  10. 201521123032 《Java程序设计》第4周学习总结

    1. 本周学习总结 1.1 尝试使用思维导图总结有关继承的知识点. 1.2 使用常规方法总结其他上课内容. 本周学习了继承,了解其中的父类与子类.了解到类,以及如何识别类,对于名词可以考虑是否创建相应 ...