1. 程式人生 > 實用技巧 >02-01 感知機

02-01 感知機

技術標籤:C#教程c#

記錄 Lambda。

兩種顯示形式:

// 第一種:表示式Lambda,右邊主體為表示式。
(parameters) => expression
// 第二種:語句Lambda,右邊主體為語句塊。
(parameters) => { < sequence - of - statements > }
“=>”Lambda宣告運算子,左邊是lambda引數,右邊是lambda主體,這三個元素構成了Lambda表示式。

所有Lambda表示式都能轉換成委託。如果Lambda表示式沒有返回值,則可以轉成Action委託型別之一。

// Action有16種過載

Action action = () => Console.Write("");
Action<int, int> action1 = (x, y) => Console.WriteLine(x*y);
Action<string, string, int> action2 = (x, y, z) => Console.Write(x);
Action<string, string, int,long> action3 = (x, y, z,m) => Console.Write(x);
如果Lambda表示式有返回值,則可以轉成Func委託型別之一。

// Func有17種過載
Func<int> func = () => 9;
Func<int, string> func1 = x => x.ToString();
Func<int, string> func2 = (x) => x.ToString();
Func<int, int,string> func3 = (x,y) => (x+y).ToString();
Func<int, int,string> func4 = (x,y) => { return (x + y).ToString();
};

使用表示式作為主體的“表示式Lambda”可以轉換為表示式樹,語句Lambda則不可以轉換為表示式樹。

System.Linq.Expressions.Expression<Func<int, int>> e = x => x * x;
System.Linq.Expressions.Expression e1 = () => Console.WriteLine("");

在需要用到委託型別或表示式的例項時,都可以使用 Lambda表示式。

// 泛型引數型別是Func<T,TResult>,根據表示式推理,則引數型別是Func<int,int>。
var squaredNumbers = numbers.Select(x=>x*x);
// Action引數
Task.Run(() => { });

引數個數為0,必須有c#教程括號。

1
Func func = () => 9;
引數個數為1,有無括號都可。

1
2
Func<int, string> func1 = x => x.ToString();
Func<int, string> func2 = (x) => x.ToString();
引數個數超1,必須有括號。

1
Action<string, string, int> action2 = (x, y, z) => Console.Write(x);

語句Lambda的主體可以包含任意數量的語句,通常不超過3句

Action<string> greet = name =>
{
    string greeting = $"Hello {name}!";
    Console.WriteLine(greeting);
};

// 不太好看
Action<string> greet1 = name =>
{
    string greeting = $"Hello {name}!";
    Console.WriteLine(greeting);
    Console.WriteLine(greeting);
    Console.WriteLine(greeting);
};

非同步的Lambda表示式,async放在引數前。

public Form1()
{
    button1.Click += async (sender, e) => await ExampleMethodAsync();

    Action<string> action = async name => { await ExampleMethodAsync(); };
    Action<string> action1 = async (name) => { await ExampleMethodAsync(); };
}

private async Task ExampleMethodAsync()
{
    await Task.Delay(1000);
}

元組型別參與的Lambda表示式(元組欄位可以取別名)

 Func<(int, int, int), (int, int, int)> doubleThem = ns => (2 * ns.Item1, 2 * ns.Item2, 2 * ns.Item3);
 var numbers = (2, 3, 4);
 var doubledNumbers = doubleThem(numbers);
 Console.WriteLine($"The set {numbers} doubled: {doubledNumbers}");

 Func<(int n1, int n2, int n3), (int a, int b, int c)> func = ns => (8 * ns.n1, 8 * ns.n2, 8 * ns.n3);
 var numbers1 = (5, 6, 7);
 var result = func(numbers1);
 var a = result.a;
 var b = result.b;
 var c = result.c;

編譯器可以推導輸入引數型別,但可以顯示指定型別。

int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 0 };
int oddNumbers = numbers.Count((int n)=>n%2==1);
Lambda型別推理

lambda引數個數與委託型別輸入引數個數要一致;Lambda 的返回值(如果有)必須能夠隱式轉換為委託的返回型別;

Lambda 中的每個輸入引數必須都能夠隱式轉換為其對應的委託引數(舉例不成功,氣死我了)

// 兩個輸入引數,Lambda包含的引數個數推理出兩個,且型別為int
Func<int, int, int> func = (x, y) => x * y;
// 推理出的引數個數不對,編譯報錯
Func<int, int, int> func1 = (x, y, z) => x * y;
// 結果型別可以隱式轉換成long
Func<int, int, long> func2 = (x, y) => x * y;
// 結果型別不能從int隱式轉換為string,編譯報錯
Func<int, int, string> func3 = (x, y) => x * y;

Lambda外部變數和變數範圍

Lambda捕獲的外部變數是否被回收,取決於引用變數的委託是否被回收。(下面程式碼的updateCapturedLocalVariable被回收,j變數就會被回收)。

Lambda引入的變數,外部無法訪問。(下面的temp變數)

Lambda無法捕獲in、out、ref引數。(下面的input如果用ref修飾,則lambda編譯報錯)

Lambda裡的goto、break、continue只能在本主體起作用,外面不能跳進來,裡面不能跳出去。

using System;

namespace ConsoleApp4
{
    class Program
    {
        public static void Main()
        {
            var game = new VariableCaptureGame();
            game.Run(90);
            // j=99
            game.updateCapturedLocalVariable(9);

            // True,依舊可以訪問j變數
            Console.WriteLine(game.isEqualToCapturedLocalVariable(99));
            // False,依舊可以訪問j變數
            Console.WriteLine(game.isEqualToCapturedLocalVariable(87));
        }
    }

    public class VariableCaptureGame
    {
        internal Action<int> updateCapturedLocalVariable;
        internal Func<int, bool> isEqualToCapturedLocalVariable;

        public void Run(int input)
        {
            int j = 0;

            updateCapturedLocalVariable = x =>
            {
                j = x + input;
          // 此變數temp,外面不能訪問
          var temp = 9; 
            };

            isEqualToCapturedLocalVariable = p => p == j;          
        }
    }
}