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. 【详细资料】ICN6211:MIPI DSI转RGB芯片简介

    ICN6211功能MIPI DSI转RGB,分辨率1920*1200,封装QFN48

  2. Redis集群的相关概念

    1.1 redis-cluster架构图 架构细节: (1)所有的redis节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽. (2)节点的fail是通过集群中超过半数的节 ...

  3. memcached常用命令

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt104 一.Memcache面向对象的常用接口包括:Memcache::con ...

  4. ReentrantLock和synchronized的性能对比

    详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytpo8 ReentrantLock和内部锁的性能对比     Reentran ...

  5. 【全面总结】js获取元素位置大小

    [js获取元素位置+元素大小]全面总结 目录 1.关于offset offsetParent(只读) offsetTop(只读) offsetLeft(只读) offsetHeight(只读) off ...

  6. ini文件必须要全路径名啊

    弄半天必须要全路径名啊.实在不行用./set.ini也可以 用UNICODE保存ini文件

  7. 201521123063 《Java程序设计》 第7周学习总结

    1. 本周学习总结 以你喜欢的方式(思维导图或其他)归纳总结集合相关内容. 2. 书面作业 ArrayList代码分析 1.1 解释ArrayList的contains源代码 public boole ...

  8. 201521123110 《Java程序设计》第7周学习总结

    1. 本章学习总结 2. 书面作业 1.ArrayList代码分析 1.1 解释ArrayList的contains源代码 public boolean contains(Object o) { re ...

  9. 201521123097《Java程序设计》第六周学习总结

    1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图,对面向对象思想进行一个总结. 2. 书面作业 1.clone方法 1.1 Object对 ...

  10. 201521123052《Java程序设计》第3周学习总结

    1. 本周学习总结 2. 书面作业 1.代码阅读 public class Test1 { private int i = 1;//这行不能修改 private static int j = 2; p ...