程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#中函數的創立和閉包的懂得

C#中函數的創立和閉包的懂得

編輯:C#入門知識

C#中函數的創立和閉包的懂得。本站提示廣大學習愛好者:(C#中函數的創立和閉包的懂得)文章只能為提供參考,不一定能成為您想要的結果。以下是C#中函數的創立和閉包的懂得正文


靜態創立函數

年夜多半同窗,都或多或少的應用過。回想下c#中靜態創立函數的退化:

C# 1.0中:


public delegate string DynamicFunction(string name);
  public static DynamicFunction GetDynamicFunction()
  {
      return GetName;
  }
  static string GetName(string name)
  {
      return name;
  }
  var result = GetDynamicFunction()("mushroom");

3.0寫慣了是否是看起來很繁瑣、落伍。 剛學拜托時,都把拜托懂得成函數指針,也來看下用函數指針完成的:


char GetName(char p);
typedef char (*DynamicFunction)(char p);
DynamicFunction GetDynamicFunction()
{
    return GetName;
}
char GetName(char p)
{
    return p;
};
char result = GetDynamicFunction()('m');

比較起來和c# 1.0簡直如出一轍了(援用/指針差異),究竟是統一家族的。

C# 2.0中,增長匿名函數:

 public delegate string DynamicFunction(string name);
      DynamicFunction result2 = delegate(string name)
      {
          return name;
      };

C# 3.0中,增長Lambda表達式,華美的回身:

 public static Func<string, string> GetDynamicFunction()
 {
        return name => name;
 }
 var result = GetDynamicFunction()("mushroom");

匿名函數缺乏的地方
固然增長Lambda表達式,曾經極年夜簡化了我們的任務量。但確切有些缺乏的地方:

var result = name => name;

這些寫編譯時是報錯的。由於c#自己強類型說話的,供給var語法糖只是為了省去聲明白定類型的任務量。 編譯器在編譯時必需可以或許完整揣摸出各參數的類型才行。代碼中的name參數類型,明顯在編譯時沒法揣摸出來的。

var result = (string name) => name;
Func<string, string> result2 = (string name) => name;
Expression<Func<string, string>> result3 = (string name) => name;

下面直接聲明name類型呢,很遺憾如許也是報錯的。代碼中曾經給出謎底了,編譯器揣摸不出左邊表達式是屬於Func<string, string>類型照樣Expression<Func<string, string>>類型。

 dynamic result = name => name;
 dynamic result1 = (Func<string,string>)(name => name);

用dynamic呢,異樣編譯器也分不出左邊是個拜托,我們顯示轉換下便可以了。

Func<string, string> function = name => name;
DynamicFunction df = function;

這裡界說個func拜托,固然參數和前往值類型都和DynamicFunction拜托一樣,但編譯時照樣會報錯:不克不及隱式轉換Func<string, string>到DynamicFunction,2個類型是不兼容的。

懂得c#中的閉包

議論到靜態創立函數,都要牽扯到閉包。閉包這個概念材料許多了,實際部門這裡就不反復了。 來看看c#代碼中閉包:

Func<Func<int>> A = () =>
        {
            var age = 18;
            return () =>  //B函數
            {
                return age;
            };
        };
        var result = A()();

下面就是閉包,可懂得為就是: 跨感化域拜訪函數內變量,也有說帶著數據的行動。
C#變量感化域一共有三種,即:類變量,實例變量,函數內變量。子感化域拜訪父感化域的變量(即函數內拜訪實例/類變量)在我們看來天經地義的,也相符我們一向的編程習氣。
例子中匿名函數B是可以拜訪下層函數A的變量age。關於編譯器而言,A函數是B函數的父感化域,所以B函數拜訪父感化域的age變量是相符標准的。

int age = 16;
        void Display()
        {
            Console.WriteLine(age); 
            int age = 18;
            Console.WriteLine(age);
        }

下面編譯會報錯未聲明應用,編譯器檢討到函數內聲明age後,感化域就會籠罩父感化域的age,(像JS就undefined了)。

        Func<int> C = () =>
         {
             var age = 19;
             return age;
         };

下面聲明個同級函數C,那末A函數是沒法訪C函數中的age變量的。 簡略來講就是弗成跨感化域拜訪其他函數內的變量。 那編譯器是怎樣完成閉包機制的呢?

如上圖,謎底是進級感化域,把A函數進級為一個實例類感化域。 在編譯代碼時代,編譯器檢討到B函數應用A函數內變量時,會主動生成一個匿名類x,把原A函數內變量age晉升為x類的字段(即實例變量),A函數晉升為匿名類x的實例函數。上面是編譯器生成的代碼(精簡過):


class Program1
{
    static Func<Func<int>> CachedAnonymousMethodDelegate2;
    static void Main(string[] args)
    {
        Func<Func<int>> func = new Func<Func<int>>(Program1.B);
        int num = func()();
    }
    static Func<int> B()
    {
        DisplayClass cl = new DisplayClass();
        cl.age = 18;
        return new Func<int>(cl.A);
    }
}
sealed class DisplayClass
{
    public int age;
    public int A()
    {
        return this.age;
    }
}

我們再來看個龐雜點的例子:


static Func<int, int> GetClosureFunction()
    {
        int val = 10;
        Func<int, int> interAdd = x => x + val;
        Console.WriteLine(interAdd(10));
        val = 30;
        Console.WriteLine(interAdd(10));
        return interAdd;
    }
  Console.WriteLine(GetClosureFunction()(30));

輸入成果是20、40、60。 當看到這個函數內變量val經由過程閉包被傳遞的時刻,我們就曉得val不只僅是個函數內變量了。之前我們剖析過編譯器怎樣生成的代碼,曉得val此時是一個匿名類的實例變量,interAdd是匿名類的實例函數。所以不管val傳遞若干層,它的值一直堅持著,直到分開這個(鏈式)感化域。

關於閉包,在js傍邊議論的比擬多,同理,可以比較懂得下:

function A() {
    var age = 18;
    return function () {
        return age;
    }
}
A()();

閉包的長處

1.對變量的掩護。想裸露一個變量值,但又怕聲明類或實例變量會被其他函數淨化,這時候便可以設計個閉包,只能經由過程函數挪用來應用它。
2.邏輯持續性和變量堅持。 A()是履行一部門邏輯,A()()僅接著A()邏輯持續走下去,在這個邏輯高低文時代,變量一直都被堅持著,可以隨便應用。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved