C# 7.0 新特性:本地方法

C# 7.0:本地方法

VS 2017 的 C# 7.0 中引入了本地方法,本地方法是一種語法糖,容許咱們在方法內定義本地方法。更加相似於函數式語言,可是,本質上仍是基於面向對象實現的。html

1. 本地方法

先看一個示例:git

 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.github

使用 ILDasm 工具,能夠看到編譯以後的結果。async

這兩個本地方法被翻譯成了兩個靜態的私有方法,它只能在定義的方法內被調用。ide

本地方法的語法定義爲:函數

<modifiers: async | unsafe> <return-type> <method-name> <parameter-list>工具

方法的修飾符只有兩種:async 和 unsafe,全部的本地方法都是私有的post

  • 若是您使用了 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. 使用本地方法實現遞歸

本地方法不須要維護調用堆棧,而遞歸方法須要維護調用堆棧,本地方法效率更高。下面的示例演示了兩種方法的區別。ui

注意:該示例使用了類型 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. 其它資源:

相關文章
相關標籤/搜索