1. 程式人生 > >C# 7.0 新特性:本地方法

C# 7.0 新特性:本地方法

性能 erro 區別 visual html 修飾 之間 style ria

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. 其它資源:

  • 官方的 本地方法 說明
  • 本地方法與 Lambda 表達式
  • C# 7.0 新特性2: 本地方法

  • Thoughts on C# 7 Local Functions

C# 7.0 新特性:本地方法