อะไรคือความแตกต่างระหว่าง lambdas และ delegates ใน. NET Framework?


88

ฉันถูกถามคำถามนี้บ่อยมากและฉันคิดว่าฉันจะขอข้อมูลบางอย่างเกี่ยวกับวิธีอธิบายความแตกต่างได้ดีที่สุด


2
โดย "ผู้รับมอบสิทธิ์" คุณหมายถึงประเภทผู้รับมอบสิทธิ์หรือผู้รับมอบสิทธิ์ที่ไม่ระบุชื่อ พวกเขายังแตกต่างกัน
Chris Ammerman


1
ทำไมผู้คนถึงทำให้คำถามของเขาซับซ้อนนัก? เพียงแค่ตอบว่าผู้รับมอบสิทธิ์คืออะไรและแลมด้าคืออะไร ให้คำอธิบายให้มากที่สุดและปล่อยให้เขาเลือกสิ่งที่เหมาะสมสำหรับเขา
Imir Hoxha

คำตอบ:


98

จริงๆแล้วมันเป็นสองสิ่งที่แตกต่างกันมาก "Delegate" เป็นชื่อของตัวแปรที่มีการอ้างอิงถึงเมธอดหรือแลมบ์ดาและแลมบ์ดาเป็นเมธอดที่ไม่มีชื่อถาวร

Lambdas ก็เหมือนกับวิธีอื่น ๆ มากยกเว้นความแตกต่างเล็กน้อย

  1. วิธีการปกติถูกกำหนดไว้ใน"คำสั่ง"และเชื่อมโยงกับชื่อถาวรในขณะที่แลมบ์ดาถูกกำหนด "ทันที" ใน"นิพจน์"และไม่มีชื่อถาวร
  2. แลมบ์ดาบางตัวสามารถใช้ได้กับทรีนิพจน์. NET ในขณะที่เมธอดไม่สามารถใช้ได้

ผู้แทนถูกกำหนดไว้เช่นนี้:

delegate Int32 BinaryIntOp(Int32 x, Int32 y);

ตัวแปรประเภท BinaryIntOp สามารถมี method หรือ labmda ที่กำหนดให้ก็ได้ตราบใดที่ลายเซ็นเหมือนกัน: อาร์กิวเมนต์ Int32 สองตัวและ Int32 return

แลมบ์ดาอาจถูกกำหนดไว้เช่นนี้:

BinaryIntOp sumOfSquares = (a, b) => a*a + b*b;

สิ่งที่ควรทราบอีกประการหนึ่งก็คือแม้ว่าประเภท Func และ Action ทั่วไปมักถูกมองว่าเป็น "ประเภทแลมบ์ดา" แต่ก็เหมือนกับผู้รับมอบสิทธิ์อื่น ๆ สิ่งที่ดีเกี่ยวกับพวกเขาคือพวกเขากำหนดชื่อให้กับผู้ร่วมประชุมประเภทใดก็ได้ที่คุณอาจต้องการ (มากถึง 4 พารามิเตอร์แม้ว่าคุณจะสามารถเพิ่มพารามิเตอร์ของคุณเองได้) ดังนั้นหากคุณกำลังใช้ประเภทผู้รับมอบสิทธิ์ที่หลากหลาย แต่ไม่มีมากกว่าหนึ่งครั้งคุณสามารถหลีกเลี่ยงการทำให้โค้ดของคุณยุ่งเหยิงด้วยการประกาศมอบสิทธิ์โดยใช้ Func และ Action

นี่คือภาพประกอบว่า Func และ Action "ไม่ใช่แค่สำหรับ lambdas":

Int32 DiffOfSquares(Int32 x, Int32 y)
{
  return x*x - y*y;
}

Func<Int32, Int32, Int32> funcPtr = DiffOfSquares;

สิ่งที่มีประโยชน์อีกประการหนึ่งที่ควรทราบคือประเภทผู้รับมอบสิทธิ์ (ไม่ใช่วิธีการเอง) ที่มีลายเซ็นเดียวกัน แต่ชื่อต่างกันจะไม่ถูกส่งต่อกันโดยปริยาย ซึ่งรวมถึงผู้รับมอบสิทธิ์ Func และ Action อย่างไรก็ตามหากลายเซ็นเหมือนกันคุณสามารถโยนระหว่างลายเซ็นได้อย่างชัดเจน

ก้าวไปอีกขั้น .... ในฟังก์ชัน C # มีความยืดหยุ่นด้วยการใช้แลมบ์ดาและผู้ร่วมประชุม แต่ C # ไม่มี "ฟังก์ชันชั้นหนึ่ง" คุณสามารถใช้ชื่อของฟังก์ชันที่กำหนดให้กับตัวแปรผู้รับมอบสิทธิ์เพื่อสร้างวัตถุที่เป็นตัวแทนของฟังก์ชันนั้น แต่มันเป็นเคล็ดลับของคอมไพเลอร์จริงๆ หากคุณเริ่มต้นคำสั่งโดยเขียนชื่อฟังก์ชันตามด้วยจุด (เช่นพยายามให้สมาชิกเข้าถึงฟังก์ชันนั้นเอง) คุณจะพบว่าไม่มีสมาชิกที่จะอ้างอิง ไม่ใช่แม้แต่คนที่มาจาก Object ซึ่งจะป้องกันไม่ให้โปรแกรมเมอร์ทำสิ่งที่เป็นประโยชน์ (และอาจเป็นอันตรายแน่นอน) เช่นการเพิ่มวิธีการขยายที่สามารถเรียกใช้กับฟังก์ชันใด ๆ สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือขยายคลาส Delegate ซึ่งก็มีประโยชน์เช่นกัน แต่ก็ไม่มากนัก

อัปเดต: ดูคำตอบของ Karg ที่แสดงความแตกต่างระหว่างตัวแทนที่ไม่ระบุชื่อกับวิธีการและแลมบ์ดา

อัปเดต 2: James Hartให้ความสำคัญแม้ว่าจะมีเทคนิคมาก แต่โปรดทราบว่า lambdas และ delegates ไม่ใช่เอนทิตี. NET (เช่น CLR ไม่มีแนวคิดเกี่ยวกับผู้รับมอบสิทธิ์หรือแลมบ์ดา) แต่เป็นเฟรมเวิร์กและโครงสร้างภาษา


คำอธิบายที่ดี แม้ว่าฉันคิดว่าคุณหมายถึง "ฟังก์ชันชั้นหนึ่ง" ไม่ใช่ "วัตถุชั้นหนึ่ง" :)
ibz

1
คุณถูก. ฉันมีโครงสร้างประโยคที่แตกต่างกันระหว่างการเขียน ("ฟังก์ชัน C # ไม่ใช่วัตถุชั้นหนึ่งจริงๆ") และลืมเปลี่ยนสิ่งนั้น ขอบคุณ!
Chris Ammerman

วิธีการปกติถูกกำหนดไว้ใน "คำสั่ง"คำสั่งคือการดำเนินการตามลำดับของโปรแกรมที่จำเป็นซึ่งอาจเป็นไปตามนิพจน์ วิธีการกำหนดโครงสร้างไวยากรณ์ที่แตกต่างกันไม่ใช่หรือ? วิธีการกำหนดไม่อยู่ในdocs.microsoft.com/en-us/dotnet/csharp/tour-of-csharp/…
Max Barraclough

32

คำถามมีความคลุมเครือเล็กน้อยซึ่งอธิบายถึงความแตกต่างอย่างกว้างขวางในคำตอบที่คุณได้รับ

คุณถามจริงว่าความแตกต่างระหว่าง lambdas และ delegates ใน. NET framework คืออะไร นั่นอาจเป็นหนึ่งในหลาย ๆ สิ่ง คุณถามว่า:

  • อะไรคือความแตกต่างระหว่างนิพจน์แลมบ์ดาและตัวแทนที่ไม่ระบุชื่อในภาษา C # (หรือ VB.NET)

  • อะไรคือความแตกต่างระหว่างอ็อบเจ็กต์ System.Linq.Expressions.LambdaExpression และอ็อบเจ็กต์ System.Delegate ใน. NET 3.5

  • หรือบางสิ่งบางอย่างระหว่างหรือรอบ ๆ สุดขั้วเหล่านั้น?

บางคนดูเหมือนจะพยายามให้คำตอบแก่คุณสำหรับคำถาม 'อะไรคือความแตกต่างระหว่างนิพจน์ C # Lambda และ. NET System.Delegate?' ซึ่งไม่สมเหตุสมผลเลย

NET Framework ไม่เข้าใจแนวคิดของผู้รับมอบสิทธิ์ที่ไม่ระบุชื่อนิพจน์แลมบ์ดาหรือการปิดซึ่งเป็นสิ่งที่กำหนดโดยข้อกำหนดทางภาษา ลองนึกถึงวิธีที่คอมไพเลอร์ C # แปลนิยามของวิธีการไม่ระบุชื่อเป็นวิธีการบนคลาสที่สร้างขึ้นโดยมีตัวแปรสมาชิกเพื่อระงับสถานะปิด ถึง. NET ไม่มีอะไรที่ไม่ระบุชื่อเกี่ยวกับผู้รับมอบสิทธิ์ เป็นเพียงการไม่ระบุตัวตนของโปรแกรมเมอร์ C # ที่เขียนมัน นั่นเป็นความจริงพอ ๆ กันกับนิพจน์แลมบ์ดาที่กำหนดให้กับประเภทผู้รับมอบสิทธิ์

อะไร. NET ไม่เข้าใจคือความคิดของผู้รับมอบสิทธิ์ให้ผู้อื่น - ประเภทที่อธิบายลายเซ็นวิธีหนึ่งอินสแตนซ์ซึ่งเป็นตัวแทนที่ถูกผูกไว้ทั้งการโทรไปยังวิธีการเฉพาะบนวัตถุที่เฉพาะเจาะจงหรือโทรไม่ได้ผูกไว้กับวิธีการหนึ่งในประเภทใดประเภทหนึ่งที่สามารถเรียกใช้กับ วัตถุประเภทนั้น ๆ โดยที่วิธีการดังกล่าวเป็นไปตามลายเซ็นดังกล่าว ประเภทดังกล่าวทั้งหมดสืบทอดมาจาก System.Delegate

.NET 3.5 ยังแนะนำเนมสเปซ System.Linq.Expressions ซึ่งมีคลาสสำหรับอธิบายนิพจน์โค้ด - และยังสามารถแสดงถึงการเรียกที่ถูกผูกไว้หรือไม่ถูกผูกไว้กับเมธอดในบางประเภทหรืออ็อบเจ็กต์ จากนั้นอินสแตนซ์ LambdaExpression สามารถคอมไพล์เป็นผู้รับมอบสิทธิ์จริงได้ (โดยวิธีการแบบไดนามิกที่ขึ้นอยู่กับโครงสร้างของนิพจน์นั้นถูกสร้างโค้ดและตัวชี้ที่มอบสิทธิ์จะถูกส่งกลับ)

ใน C # คุณสามารถสร้างอินสแตนซ์ของ System.Expressions.Expression types โดยการกำหนด lambda expression ให้กับตัวแปรชนิดดังกล่าวซึ่งจะสร้างโค้ดที่เหมาะสมเพื่อสร้างนิพจน์ที่รันไทม์

แน่นอนถ้าคุณถูกขอให้สิ่งที่แตกต่างระหว่างการแสดงออกแลมบ์ดาและวิธีการที่ไม่ระบุชื่อใน C # หลังจากทั้งหมดแล้วทั้งหมดนี้เป็น irelevant สวยมากและในกรณีที่ความแตกต่างหลักเป็นระยะเวลาสั้น ๆ ซึ่งชะโงกตัวไปได้รับมอบหมายที่ไม่ระบุชื่อเมื่อคุณสวม' ไม่สนใจเกี่ยวกับพารามิเตอร์และไม่ได้วางแผนที่จะส่งคืนค่าและไปยัง lambdas เมื่อคุณต้องการพิมพ์พารามิเตอร์ที่อ้างอิงและประเภทการส่งคืน

และแลมบ์ดานิพจน์สนับสนุนการสร้างนิพจน์


3
ข้อมูลเยี่ยม! คุณเป็นแรงบันดาลใจให้ฉันยิงตัวสะท้อนแสงและมองไปที่ IL ฉันไม่รู้ว่า lambdas ส่งผลให้เกิดคลาสที่สร้างขึ้น แต่ตอนนี้ฉันคิดเกี่ยวกับเรื่องนี้แล้ว
Chris Ammerman

21

ความแตกต่างอย่างหนึ่งคือผู้รับมอบสิทธิ์ที่ไม่ระบุชื่อสามารถละเว้นพารามิเตอร์ได้ในขณะที่แลมบ์ดาต้องตรงกับลายเซ็นที่แน่นอน ให้:

public delegate string TestDelegate(int i);

public void Test(TestDelegate d)
{}

คุณสามารถเรียกมันได้ในสี่วิธีต่อไปนี้ (โปรดทราบว่าบรรทัดที่สองมีผู้รับมอบสิทธิ์ที่ไม่ระบุตัวตนซึ่งไม่มีพารามิเตอร์ใด ๆ ):

Test(delegate(int i) { return String.Empty; });
Test(delegate { return String.Empty; });
Test(i => String.Empty);
Test(D);

private string D(int i)
{
    return String.Empty;
}

คุณไม่สามารถส่งผ่านในนิพจน์แลมบ์ดาที่ไม่มีพารามิเตอร์หรือวิธีการที่ไม่มีพารามิเตอร์ สิ่งเหล่านี้ไม่ได้รับอนุญาต:

Test(() => String.Empty); //Not allowed, lambda must match signature
Test(D2); //Not allowed, method must match signature

private string D2()
{
    return String.Empty;
}

13

ผู้รับมอบสิทธิ์เทียบเท่ากับตัวชี้ฟังก์ชัน / ตัวชี้วิธีการ / การเรียกกลับ (เลือกของคุณ) และ lambdas เป็นฟังก์ชันที่ไม่ระบุตัวตนที่ค่อนข้างง่าย อย่างน้อยนั่นคือสิ่งที่ฉันบอกผู้คน


เป๊ะ! ไม่มี "ความแตกต่าง" เป็นสองสิ่งที่แตกต่างกันโดยเนื้อแท้
ibz

3

ฉันไม่มีประสบการณ์มากมายกับสิ่งนี้ แต่วิธีที่ฉันจะอธิบายก็คือผู้รับมอบสิทธิ์เป็นเครื่องห่อหุ้มฟังก์ชันใด ๆ ในขณะที่นิพจน์แลมบ์ดาเป็นฟังก์ชันที่ไม่ระบุตัวตน


3

ผู้รับมอบสิทธิ์เป็นเพียงตัวชี้ฟังก์ชันเท่านั้น แลมบ์ดาสามารถเปลี่ยนเป็นผู้รับมอบสิทธิ์ได้ แต่ก็สามารถเปลี่ยนเป็นแผนภูมินิพจน์ LINQ ได้เช่นกัน ตัวอย่างเช่น

Func<int, int> f = x => x + 1;
Expression<Func<int, int>> exprTree = x => x + 1;

บรรทัดแรกสร้างตัวแทนในขณะที่บรรทัดที่สองสร้างแผนภูมินิพจน์


2
นั่นคือความจริง แต่ความแตกต่างระหว่างพวกเขาคือการที่พวกเขาเป็นสองแนวคิดที่แตกต่างอย่างสิ้นเชิง นี่ก็เหมือนกับการเปรียบเทียบแอปเปิ้ลกับส้ม ดูคำตอบของ Dan Shield
ibz

2

lambdas เป็นเพียงน้ำตาลที่เป็นประโยคต่อตัวแทน คอมไพเลอร์ลงท้ายด้วยการแปลง lambdas เป็นผู้รับมอบสิทธิ์

สิ่งเหล่านี้เหมือนกันฉันเชื่อว่า:

Delegate delegate = x => "hi!";
Delegate delegate = delegate(object x) { return "hi";};

2
ตัวอย่างเหล่านี้ไม่ได้รวบรวม แม้ว่าคุณจะเปลี่ยนชื่อของอินสแตนซ์Delegateจาก 'delegate' ซึ่งเป็นคีย์เวิร์ด
Steve Cooper

2

ผู้รับมอบสิทธิ์คือลายเซ็นของฟังก์ชัน สิ่งที่ต้องการ

delegate string MyDelegate(int param1);

ผู้รับมอบสิทธิ์ไม่ได้ใช้ร่างกาย

แลมบ์ดาเป็นการเรียกใช้ฟังก์ชันที่ตรงกับลายเซ็นของผู้รับมอบสิทธิ์ สำหรับผู้รับมอบสิทธิ์ข้างต้นคุณอาจใช้;

(int i) => i.ToString();
(int i) => "ignored i";
(int i) => "Step " + i.ToString() + " of 10";

Delegateประเภทเป็นชื่อที่ตั้งไม่ดีแม้ว่า; การสร้างออบเจ็กต์ประเภทDelegateสร้างตัวแปรที่สามารถเก็บฟังก์ชันได้ไม่ว่าจะเป็น lambdas วิธีการแบบคงที่หรือวิธีคลาส


เมื่อคุณสร้างตัวแปรประเภท MyDelegate นั่นไม่ใช่ประเภทรันไทม์จริงๆ ประเภทรันไทม์คือ Delegate มีกลเม็ดของคอมไพเลอร์ที่เกี่ยวข้องกับการรวบรวมตัวแทน lambdas และนิพจน์ทรีซึ่งฉันคิดว่าสาเหตุที่ทำให้โค้ดบ่งบอกถึงสิ่งที่ไม่เป็นความจริง
Chris Ammerman

2

ผู้รับมอบสิทธิ์คือการอ้างอิงถึงวิธีการที่มีรายการพารามิเตอร์เฉพาะและประเภทการส่งคืน อาจรวมถึงวัตถุหรือไม่ก็ได้

แลมบ์ดานิพจน์เป็นรูปแบบของฟังก์ชันที่ไม่ระบุชื่อ


2

ผู้รับมอบสิทธิ์คือคิวของตัวชี้ฟังก์ชันการเรียกใช้ผู้รับมอบสิทธิ์อาจเรียกใช้หลายวิธี แลมบ์ดาเป็นการประกาศเมธอดแบบไม่ระบุตัวตนซึ่งคอมไพลเลอร์อาจตีความแตกต่างกันขึ้นอยู่กับบริบทที่ใช้เป็น

คุณสามารถรับผู้รับมอบสิทธิ์ที่ชี้ไปที่นิพจน์แลมบ์ดาเป็นวิธีการโดยการแคสต์ลงในผู้รับมอบสิทธิ์หรือหากส่งต่อเป็นพารามิเตอร์ไปยังเมธอดที่คาดว่าจะมีประเภทผู้รับมอบสิทธิ์เฉพาะคอมไพเลอร์จะส่งให้คุณ การใช้มันภายในคำสั่ง LINQ แลมบ์ดาจะถูกแปลโดยคอมไพเลอร์เป็นแผนภูมินิพจน์แทนที่จะเป็นเพียงผู้รับมอบสิทธิ์

ความแตกต่างจริงๆคือแลมบ์ดาเป็นวิธีที่สั้นในการกำหนดวิธีการภายในนิพจน์อื่นในขณะที่ผู้รับมอบสิทธิ์เป็นประเภทวัตถุจริง


2

เป็นที่ชัดเจนว่าคำถามควรจะเป็น "lambdas กับanonymousต่างกันอย่างไรผู้ได้รับมอบหมายที่อย่างไร" จากคำตอบทั้งหมดที่นี่มีเพียงคนเดียวเท่านั้นที่ทำให้ถูกต้อง - ความแตกต่างที่สำคัญคือ lambdas สามารถใช้เพื่อสร้างต้นไม้แสดงออกเช่นเดียวกับผู้รับมอบสิทธิ์

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับ MSDN: http://msdn.microsoft.com/en-us/library/bb397687.aspx


1

ผู้รับมอบสิทธิ์เป็นเพียงการพิมพ์โครงสร้างสำหรับฟังก์ชัน คุณสามารถทำสิ่งเดียวกันกับการพิมพ์เล็กน้อยและใช้คลาสที่ไม่ระบุตัวตนที่ใช้อินเทอร์เฟซหรือคลาสนามธรรม แต่จะกลายเป็นโค้ดจำนวนมากเมื่อต้องการเพียงฟังก์ชันเดียว

แลมด้ามาจากแนวคิดเรื่องแคลคูลัสแลมบ์ดาของคริสตจักรอลองโซในทศวรรษที่ 1930 เป็นวิธีการสร้างฟังก์ชันแบบไม่ระบุตัวตน สิ่งเหล่านี้มีประโยชน์อย่างยิ่งสำหรับการเขียนฟังก์ชัน

ดังนั้นในขณะที่บางคนอาจบอกว่าแลมบ์ดาเป็นน้ำตาลเชิงไวยากรณ์สำหรับผู้ร่วมประชุม แต่ฉันก็บอกว่าผู้ได้รับมอบหมายเป็นสะพานในการผ่อนคลายผู้คนให้เป็นแลมบ์ดาใน c #


1

พื้นฐานบางอย่างที่นี่ "Delegate" เป็นชื่อของตัวแปรที่มีการอ้างอิงถึงเมธอดหรือแลมบ์ดา

นี่เป็นวิธีที่ไม่ระบุตัวตน -

(string testString) => { Console.WriteLine(testString); };

เนื่องจากวิธีการที่ไม่ระบุชื่อไม่มีชื่อใด ๆ เราจึงต้องมีผู้รับมอบสิทธิ์ซึ่งเราสามารถกำหนดวิธีการหรือนิพจน์ทั้งสองนี้ได้ สำหรับ Ex.

delegate void PrintTestString(string testString); // declare a delegate

PrintTestString print = (string testString) => { Console.WriteLine(testString); }; 
print();

เช่นเดียวกับนิพจน์แลมบ์ดา โดยปกติแล้วเราต้องการผู้รับมอบสิทธิ์เพื่อใช้งาน

s => s.Age > someValue && s.Age < someValue    // will return true/false

เราสามารถใช้ func delegate เพื่อใช้สำนวนนี้

Func< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;

bool result = checkStudentAge ( Student Object);

0

Lambdas เป็นเวอร์ชันที่เรียบง่ายของผู้ร่วมประชุม พวกเขามีคุณสมบัติบางอย่างของการปิดเช่นผู้รับมอบสิทธิ์ที่ไม่ระบุชื่อ แต่ยังช่วยให้คุณใช้การพิมพ์โดยนัยได้ แลมด้าเช่นนี้:

something.Sort((x, y) => return x.CompareTo(y));

มีความกระชับมากกว่าสิ่งที่คุณสามารถทำได้กับผู้รับมอบสิทธิ์:

something.Sort(sortMethod);
...

private int sortMethod(SomeType one, SomeType two)
{
    one.CompareTo(two)
}

คุณหมายถึง lambdas เป็นเหมือนวิธีการแบบไม่ระบุตัวตนที่เรียบง่าย (ไม่ใช่ตัวแทน) เช่นเดียวกับวิธีการ (ไม่ระบุชื่อหรือไม่) สามารถกำหนดให้กับตัวแปรผู้รับมอบสิทธิ์
Lucas

0

นี่เป็นตัวอย่างที่ฉันวางไว้ในบล็อกง่อย ๆ ของฉัน สมมติว่าคุณต้องการอัปเดตป้ายกำกับจากเธรดผู้ปฏิบัติงาน ฉันมี 4 ตัวอย่างวิธีการอัปเดตป้ายกำกับนั้นจาก 1 ถึง 50 โดยใช้ตัวแทนผู้ร่วมประชุมอานนท์และแลมบ์ดา 2 ประเภท

 private void button2_Click(object sender, EventArgs e) 
     { 
         BackgroundWorker worker = new BackgroundWorker(); 
         worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
         worker.RunWorkerAsync(); 
     } 

     private delegate void UpdateProgDelegate(int count); 
     private void UpdateText(int count) 
     { 
         if (this.lblTest.InvokeRequired) 
         { 
             UpdateProgDelegate updateCallBack = new UpdateProgDelegate(UpdateText); 
             this.Invoke(updateCallBack, new object[] { count }); 
         } 
         else 
         { 
             lblTest.Text = count.ToString(); 
         } 
     } 

     void worker_DoWork(object sender, DoWorkEventArgs e) 
     {   
         /* Old Skool delegate usage.  See above for delegate and method definitions */ 
         for (int i = 0; i < 50; i++) 
         { 
             UpdateText(i); 
             Thread.Sleep(50); 
         } 

         // Anonymous Method 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke((MethodInvoker)(delegate() 
             { 
                 lblTest.Text = i.ToString(); 
             })); 
             Thread.Sleep(50); 
         } 

         /* Lambda using the new Func delegate. This lets us take in an int and 
          * return a string.  The last parameter is the return type. so 
          * So Func<int, string, double> would take in an int and a string 
          * and return a double.  count is our int parameter.*/ 
         Func<int, string> UpdateProgress = (count) => lblTest.Text = count.ToString(); 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke(UpdateProgress, i); 
             Thread.Sleep(50); 
         } 

         /* Finally we have a totally inline Lambda using the Action delegate 
          * Action is more or less the same as Func but it returns void. We could 
          * use it with parameters if we wanted to like this: 
          * Action<string> UpdateProgress = (count) => lblT…*/ 
         for (int i = 0; i < 50; i++) 
         { 
             lblTest.Invoke((Action)(() => lblTest.Text = i.ToString())); 
             Thread.Sleep(50); 
         } 
     }

0

ฉันคิดว่าคำถามของคุณเกี่ยวข้องกับ c # ไม่ใช่. NET เนื่องจากความคลุมเครือของคำถามของคุณเนื่องจาก. NET ไม่ได้อยู่คนเดียว - นั่นคือการไม่มี c # - ความเข้าใจในตัวแทนและการแสดงออกของแลมบ์ดา

A ( ปกติในทางตรงกันข้ามกับที่เรียกว่าผู้รับมอบสิทธิ์ทั่วไปcf ในภายหลัง) ผู้รับมอบสิทธิ์ควรถูกมองว่าเป็นชนิดของ c ++ typedefของประเภทตัวชี้ฟังก์ชันตัวอย่างเช่นใน c ++:

R (*thefunctionpointer) ( T ) ;

typedef เป็นชนิดthefunctionpointerซึ่งเป็นประเภทของตัวชี้ไปยังฟังก์ชั่นการใช้วัตถุชนิดนั้นและกลับมาวัตถุของการพิมพ์T Rคุณจะใช้มันดังนี้:

thefunctionpointer = &thefunction ;
R r = (*thefunctionpointer) ( t ) ; // where t is of type T

ซึ่งthefunctionจะเป็นฟังก์ชันที่รับTและส่งคืนไฟล์Rไฟล์.

ใน c # คุณจะไป

delegate R thedelegate( T t ) ; // and yes, here the identifier t is needed

และคุณจะใช้มันในลักษณะนี้:

thedelegate thedel = thefunction ;
R r = thedel ( t ) ; // where t is of type T

ซึ่งthefunctionจะเป็นฟังก์ชันที่รับTและส่งคืนไฟล์Rไฟล์. สำหรับผู้ได้รับมอบหมายหรือที่เรียกว่าผู้รับมอบสิทธิ์ปกติ

ตอนนี้คุณยังมีตัวแทนทั่วไปใน c # ซึ่งเป็นผู้รับมอบสิทธิ์ที่เป็นแบบทั่วไปกล่าวคือ "เทมเพลต" เพื่อที่จะพูดโดยใช้นิพจน์ c ++ พวกเขาถูกกำหนดไว้เช่นนี้:

public delegate TResult Func<in T, out TResult>(T arg);

และคุณสามารถใช้มันได้ดังนี้:

Func<double, double> thefunctor = thefunction2; // call it a functor because it is
                                                // really as a functor that you should
                                                // "see" it
double y = thefunctor(2.0);

ซึ่งthefunction2เป็นฟังก์ชั่นการใช้เป็นอาร์กิวเมนต์และกลับdoubleเป็นฟังก์ชั่นการใช้เป็นอาร์กิวเมนต์และกลับ

ทีนี้ลองนึกดูว่าแทนที่จะthefunction2ใช้ "ฟังก์ชัน" ที่ไม่มีที่ไหนกำหนดไว้สำหรับตอนนี้ด้วยคำสั่งและฉันจะไม่ใช้ในภายหลัง จากนั้น c # ให้เราใช้นิพจน์ของฟังก์ชันนี้ โดยการแสดงออกของผมหมายถึง "คณิตศาสตร์" (หรือการทำงานที่จะติดโปรแกรม) การแสดงออกของมันเช่น: ไปdouble xผมจะเชื่อมโยง double x*xในวิชาคณิตศาสตร์ที่คุณเขียนนี้โดยใช้"การ \ mapsto สัญลักษณ์" ใน c # มีการยืมสัญกรณ์การทำงาน: =>. ตัวอย่างเช่น:

Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
                                                           // mandatory

(double x) => x * xคือการแสดงออกการแสดงออกไม่ใช่ประเภทในขณะที่ผู้รับมอบสิทธิ์ (ทั่วไปหรือไม่) เป็น

ศีลธรรม? ในตอนท้ายผู้รับมอบสิทธิ์คืออะไร (resp. generic delegate) ถ้าไม่ใช่ประเภทตัวชี้ฟังก์ชัน (resp. wrap + smart + ประเภทตัวชี้ฟังก์ชันทั่วไป) ห๊ะ? อื่น ๆ อีก ! ดูสิ่งนี้และสิ่งนั้น


-1

เวอร์ชันที่ย่อมากเกินไปคือแลมบ์ดาเป็นเพียงชวเลขสำหรับฟังก์ชันที่ไม่ระบุตัวตน ผู้รับมอบสิทธิ์สามารถทำหน้าที่ได้มากกว่าฟังก์ชันที่ไม่ระบุตัวตน: สิ่งต่างๆเช่นเหตุการณ์การโทรแบบอะซิงโครนัสและเครือข่ายวิธีการต่างๆ


1
lambdas สามารถใช้เป็นตัวจัดการเหตุการณ์ button.Click + = (ผู้ส่ง, eventArgs) => {MessageBox.Show ("คลิก"); } และเรียกใหม่ว่า System.Threading.Thread (() => Console.Write ("Executed on a thread")). start ();
Steve Cooper
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.