Tôi đã hỏi câu hỏi này rất nhiều và tôi nghĩ rằng tôi sẽ trưng cầu một số đầu vào về cách mô tả tốt nhất sự khác biệt.
Tôi đã hỏi câu hỏi này rất nhiều và tôi nghĩ rằng tôi sẽ trưng cầu một số đầu vào về cách mô tả tốt nhất sự khác biệt.
Câu trả lời:
Chúng thực sự là hai thứ rất khác nhau. "Delegate" thực sự là tên của một biến chứa một tham chiếu đến một phương thức hoặc một lambda và lambda là một phương thức không có tên cố định.
Lambdas rất giống các phương pháp khác, ngoại trừ một vài khác biệt nhỏ.
Một đại biểu được định nghĩa như thế này:
delegate Int32 BinaryIntOp(Int32 x, Int32 y);
Một biến kiểu BinaryIntOp có thể có một phương thức hoặc một labmda được gán cho nó, miễn là chữ ký giống nhau: hai đối số Int32 và một trả về Int32.
Một lambda có thể được định nghĩa như thế này:
BinaryIntOp sumOfSquares = (a, b) => a*a + b*b;
Một điều cần lưu ý nữa là mặc dù các kiểu Func và Action chung thường được coi là "kiểu lambda", chúng cũng giống như bất kỳ đại biểu nào khác. Điều thú vị về chúng là chúng về cơ bản xác định tên cho bất kỳ loại đại biểu nào bạn có thể cần (tối đa 4 tham số, mặc dù bạn chắc chắn có thể thêm nhiều tham số của riêng mình). Vì vậy, nếu bạn đang sử dụng nhiều loại ủy quyền, nhưng không nhiều hơn một lần, bạn có thể tránh làm lộn xộn mã của mình với các khai báo ủy quyền bằng cách sử dụng Hàm và Hành động.
Đây là một minh họa về cách Func và Action "không chỉ dành cho lambdas":
Int32 DiffOfSquares(Int32 x, Int32 y)
{
return x*x - y*y;
}
Func<Int32, Int32, Int32> funcPtr = DiffOfSquares;
Một điều hữu ích khác cần biết là các kiểu ủy nhiệm (không phải chính các phương thức) có cùng chữ ký nhưng tên khác nhau sẽ không được truyền ngầm cho nhau. Điều này bao gồm các đại biểu Hàm và Hành động. Tuy nhiên, nếu chữ ký giống hệt nhau, bạn có thể truyền rõ ràng giữa chúng.
Đi xa hơn .... Trong C #, các hàm rất linh hoạt, với việc sử dụng lambdas và các đại biểu. Nhưng C # không có "hàm hạng nhất". Bạn có thể sử dụng tên của một hàm được gán cho một biến ủy nhiệm để tạo một đối tượng đại diện cho hàm đó về cơ bản. Nhưng nó thực sự là một thủ thuật biên dịch. Nếu bạn bắt đầu một câu lệnh bằng cách viết tên hàm theo sau là dấu chấm (tức là cố gắng thực hiện quyền truy cập thành viên trên chính hàm), bạn sẽ thấy không có thành viên nào ở đó để tham chiếu. Thậm chí không phải những cái từ Object. Điều này ngăn lập trình viên làm những việc hữu ích (và tất nhiên là có thể nguy hiểm) như thêm các phương thức mở rộng có thể được gọi trên bất kỳ hàm nào. Điều tốt nhất bạn có thể làm là mở rộng chính lớp Delegate, điều này chắc chắn cũng hữu ích, nhưng không hoàn toàn nhiều.
Cập nhật: Cũng xem câu trả lời của Karg minh họa sự khác biệt giữa các đại biểu ẩn danh so với các phương thức & lambdas.
Cập nhật 2: James Hart đưa ra một điều quan trọng, mặc dù rất kỹ thuật, lưu ý rằng lambdas và các đại biểu không phải là các thực thể .NET (tức là CLR không có khái niệm về một đại biểu hoặc lambda), mà chúng là các cấu trúc khung và ngôn ngữ.
Câu hỏi hơi mơ hồ, điều này giải thích sự chênh lệch lớn trong các câu trả lời bạn nhận được.
Bạn đã thực sự hỏi sự khác biệt giữa lambdas và các đại biểu trong .NET framework; đó có thể là một trong nhiều thứ. Bạn đang hỏi tôi sao:
Sự khác biệt giữa biểu thức lambda và đại biểu ẩn danh trong ngôn ngữ C # (hoặc VB.NET) là gì?
Sự khác biệt giữa các đối tượng System.Linq.Expressions.LambdaExpression và các đối tượng System.Delegate trong .NET 3.5 là gì?
Hoặc một cái gì đó ở đâu đó giữa hoặc xung quanh những thái cực đó?
Một số người dường như đang cố gắng cung cấp cho bạn câu trả lời cho câu hỏi 'sự khác biệt giữa biểu thức C # Lambda và .NET System.Delegate là gì?', Điều này hoàn toàn không có ý nghĩa.
Bản thân khung công tác .NET không hiểu các khái niệm về đại biểu ẩn danh, biểu thức lambda hoặc bao đóng - đó là tất cả những thứ được xác định bởi đặc tả ngôn ngữ. Hãy nghĩ về cách trình biên dịch C # chuyển định nghĩa của một phương thức ẩn danh thành một phương thức trên một lớp được tạo với các biến thành viên để giữ trạng thái đóng; với .NET, không có gì ẩn danh về người đại diện; nó chỉ ẩn danh đối với lập trình viên C # viết nó. Điều đó cũng đúng với biểu thức lambda được gán cho kiểu đại biểu.
.NET DOES hiểu là ý tưởng về một đại biểu - một kiểu mô tả một chữ ký phương thức, các trường hợp của nó đại diện cho các lệnh gọi liên kết đến các phương thức cụ thể trên các đối tượng cụ thể hoặc các lệnh gọi không liên kết đến một phương thức cụ thể trên một kiểu cụ thể có thể được gọi ra bất kỳ đối tượng nào thuộc loại đó, trong đó phương pháp đã nói tuân theo chữ ký đã nói. Tất cả các kiểu như vậy đều kế thừa từ System.Delegate.
.NET 3.5 cũng giới thiệu không gian tên System.Linq.Expressions, chứa các lớp để mô tả các biểu thức mã - và do đó cũng có thể đại diện cho các lệnh gọi liên kết hoặc không liên kết đến các phương thức trên các kiểu hoặc đối tượng cụ thể. Các cá thể LambdaExpression sau đó có thể được biên dịch thành các đại biểu thực tế (theo đó một phương thức động dựa trên cấu trúc của biểu thức được tạo mã và một con trỏ đại biểu tới nó được trả về).
Trong C #, bạn có thể tạo các phiên bản của kiểu System.Expressions.Expression bằng cách gán một biểu thức lambda cho một biến của kiểu đã nói, biến này sẽ tạo ra mã thích hợp để tạo biểu thức trong thời gian chạy.
Tất nhiên, nếu bạn đang hỏi sự khác biệt giữa các biểu thức lambda và các phương thức ẩn danh trong C #, thì tất cả những điều này khá không liên quan và trong trường hợp đó, sự khác biệt chính là sự ngắn gọn, nghiêng về các đại biểu ẩn danh khi bạn không không quan tâm đến các tham số và không có kế hoạch trả về một giá trị và hướng tới lambdas khi bạn muốn nhập các tham số được tham chiếu và các kiểu trả về.
Và các biểu thức lambda hỗ trợ tạo biểu thức.
Một điểm khác biệt là một đại biểu ẩn danh có thể bỏ qua các tham số trong khi lambda phải khớp với chữ ký chính xác. Được:
public delegate string TestDelegate(int i);
public void Test(TestDelegate d)
{}
bạn có thể gọi nó theo bốn cách sau (lưu ý rằng dòng thứ hai có một đại biểu ẩn danh không có bất kỳ tham số nào):
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;
}
Bạn không thể chuyển vào biểu thức lambda không có tham số hoặc phương thức không có tham số. Chúng không được phép:
Test(() => String.Empty); //Not allowed, lambda must match signature
Test(D2); //Not allowed, method must match signature
private string D2()
{
return String.Empty;
}
Các đại biểu tương đương với con trỏ hàm / con trỏ phương thức / lệnh gọi lại (tùy chọn của bạn) và lambdas là các hàm ẩn danh được đơn giản hóa khá nhiều. Ít nhất đó là những gì tôi nói với mọi người.
Về cơ bản, một đại biểu luôn chỉ là một con trỏ hàm. Một lambda có thể biến thành một đại biểu, nhưng nó cũng có thể biến thành một cây biểu thức LINQ. Ví dụ,
Func<int, int> f = x => x + 1;
Expression<Func<int, int>> exprTree = x => x + 1;
Dòng đầu tiên tạo ra một đại biểu, trong khi dòng thứ hai tạo ra một cây biểu thức.
lambdas chỉ đơn giản là đường cú pháp trên một đại biểu. Trình biên dịch kết thúc việc chuyển đổi lambdas thành các đại biểu.
Những điều này đều giống nhau, tôi tin rằng:
Delegate delegate = x => "hi!";
Delegate delegate = delegate(object x) { return "hi";};
Delegate
từ 'Delegate', là một từ khóa.
Một đại biểu là một chữ ký hàm; cái gì đó như
delegate string MyDelegate(int param1);
Người ủy quyền không thực hiện một phần thân.
Lambda là một lệnh gọi hàm khớp với chữ ký của người được ủy quyền. Đối với đại biểu ở trên, bạn có thể sử dụng bất kỳ;
(int i) => i.ToString();
(int i) => "ignored i";
(int i) => "Step " + i.ToString() + " of 10";
Các Delegate
loại được nặng được đặt tên, mặc dù; tạo một đối tượng kiểu Delegate
thực sự tạo ra một biến có thể chứa các hàm - có thể là lambdas, phương thức tĩnh hoặc phương thức lớp.
Một ủy nhiệm là một tham chiếu đến một phương thức với một danh sách tham số cụ thể và kiểu trả về. Nó có thể có hoặc không bao gồm một đối tượng.
Biểu thức lambda là một dạng của hàm ẩn danh.
Một đại biểu là một Hàng đợi của các con trỏ hàm, việc gọi một đại biểu có thể gọi ra nhiều phương thức. Lambda về cơ bản là một khai báo phương thức ẩn danh có thể được trình biên dịch diễn giải theo cách khác nhau, tùy thuộc vào ngữ cảnh mà nó được sử dụng.
Bạn có thể lấy một ủy nhiệm trỏ đến biểu thức lambda dưới dạng một phương thức bằng cách truyền nó thành một ủy quyền hoặc nếu chuyển nó vào dưới dạng một tham số cho một phương thức mong đợi một kiểu đại biểu cụ thể, trình biên dịch sẽ truyền nó cho bạn. Sử dụng nó bên trong câu lệnh LINQ, lambda sẽ được trình biên dịch dịch thành cây biểu thức thay vì chỉ đơn giản là một đại biểu.
Sự khác biệt thực sự là lambda là một cách ngắn gọn để xác định một phương thức bên trong một biểu thức khác, trong khi một đại biểu là một kiểu đối tượng thực tế.
Rõ ràng câu hỏi được đặt ra là "sự khác biệt giữa lambdas và các đại biểu ẩn danh là gì?" Trong số tất cả các câu trả lời ở đây chỉ có một người đúng - sự khác biệt chính là lambdas có thể được sử dụng để tạo cây biểu thức cũng như các đại biểu.
Bạn có thể đọc thêm trên MSDN: http://msdn.microsoft.com/en-us/library/bb397687.aspx
Các đại biểu thực sự chỉ là việc nhập cấu trúc cho các hàm. Bạn có thể làm điều tương tự với cách nhập danh nghĩa và triển khai một lớp ẩn danh triển khai một giao diện hoặc lớp trừu tượng, nhưng điều đó sẽ trở thành rất nhiều mã khi chỉ cần một chức năng.
Lambda xuất phát từ ý tưởng về phép tính lambda của Nhà thờ Alonzo vào những năm 1930. Đó là một cách ẩn danh để tạo các chức năng. Chúng trở nên đặc biệt hữu ích cho các chức năng soạn thảo
Vì vậy, trong khi một số người có thể nói lambda là đường cú pháp cho các đại biểu, tôi sẽ nói rằng các đại biểu là một cầu nối để đưa mọi người vào lambda trong c #.
Một số cơ bản ở đây. "Delegate" thực sự là tên của một biến chứa tham chiếu đến một phương thức hoặc lambda
Đây là một phương pháp ẩn danh -
(string testString) => { Console.WriteLine(testString); };
Vì phương thức ẩn danh không có bất kỳ tên nào nên chúng ta cần một đại biểu để có thể gán cả hai phương thức hoặc biểu thức này. Đối với Ex.
delegate void PrintTestString(string testString); // declare a delegate
PrintTestString print = (string testString) => { Console.WriteLine(testString); };
print();
Tương tự với biểu thức lambda. Thông thường, chúng tôi cần ủy quyền để sử dụng chúng
s => s.Age > someValue && s.Age < someValue // will return true/false
Chúng ta có thể sử dụng một đại biểu func để sử dụng biểu thức này.
Func< Student,bool> checkStudentAge = s => s.Age > someValue && s.Age < someValue ;
bool result = checkStudentAge ( Student Object);
Lambdas là phiên bản đơn giản hóa của các đại biểu. Chúng có một số thuộc tính của một bao đóng như các đại biểu ẩn danh, nhưng cũng cho phép bạn sử dụng cách nhập ngụ ý. Một lambda như thế này:
something.Sort((x, y) => return x.CompareTo(y));
ngắn gọn hơn rất nhiều so với những gì bạn có thể làm với một người được ủy quyền:
something.Sort(sortMethod);
...
private int sortMethod(SomeType one, SomeType two)
{
one.CompareTo(two)
}
Đây là một ví dụ mà tôi đã đưa lên một lúc trên blog của mình. Giả sử bạn muốn cập nhật nhãn từ chuỗi công nhân. Tôi có 4 ví dụ về cách cập nhật nhãn đó từ 1 đến 50 bằng cách sử dụng đại biểu, đại biểu anon và 2 loại lambdas.
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);
}
}
Tôi giả định rằng câu hỏi của bạn liên quan đến c # chứ không phải .NET, vì câu hỏi của bạn không rõ ràng, vì .NET không đơn độc - nghĩa là, không có c # - sự hiểu biết của đại biểu và biểu thức lambda.
Một đại biểu ( bình thường , đối lập với cái gọi là đại biểu chung , cf sau này) phải được coi là một loại c ++ typedef
của kiểu con trỏ hàm, ví dụ: trong c ++:
R (*thefunctionpointer) ( T ) ;
typedef là kiểu thefunctionpointer
là kiểu con trỏ đến một hàm nhận một đối tượng kiểu T
và trả về một đối tượng kiểu R
. Bạn sẽ sử dụng nó như thế này:
thefunctionpointer = &thefunction ;
R r = (*thefunctionpointer) ( t ) ; // where t is of type T
đâu thefunction
sẽ là một hàm lấy a T
và trả về R
.
Trong c # bạn sẽ đi cho
delegate R thedelegate( T t ) ; // and yes, here the identifier t is needed
và bạn sẽ sử dụng nó như thế này:
thedelegate thedel = thefunction ;
R r = thedel ( t ) ; // where t is of type T
đâu thefunction
sẽ là một hàm lấy a T
và trả về R
. Cái này dành cho đại biểu, nên gọi là đại biểu bình thường.
Bây giờ, bạn cũng có các đại biểu chung trong c #, là các đại biểu chung chung, tức là được "tạo khuôn mẫu" để nói, bằng cách sử dụng biểu thức c ++. Chúng được định nghĩa như thế này:
public delegate TResult Func<in T, out TResult>(T arg);
Và bạn có thể sử dụng chúng như thế này:
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);
đâu thefunction2
là một hàm nhận làm đối số và trả về a double
.
Bây giờ, hãy tưởng tượng rằng thay vì thefunction2
tôi muốn sử dụng một "chức năng" hiện chưa được định nghĩa bằng một câu lệnh, và tôi sẽ không bao giờ sử dụng sau này. Sau đó c # cho phép chúng ta sử dụng biểu thức của hàm này. Bằng cách biểu hiện tôi có nghĩa là "toán học" (hoặc chức năng, gắn bó với các chương trình) biểu hiện của nó, ví dụ: để một double x
tôi sẽ liên kết các double
x*x
. Trong toán học, bạn viết điều này bằng cách sử dụng ký hiệu latex "\ mapsto" . Trong c # các ký hiệu chức năng đã được mượn: =>
. Ví dụ :
Func<double, double> thefunctor = ( (double x) => x * x ); // outer brackets are not
// mandatory
(double x) => x * x
là một biểu thức . Nó không phải là một kiểu, trong khi các đại biểu (chung chung hoặc không).
Đạo đức ? Cuối cùng, đại biểu (tương ứng với đại biểu chung) là gì, nếu không phải là loại con trỏ hàm (loại con trỏ hàm tương ứng + thông minh + chung), hả? Thứ gì khác ! Xem cái này và cái kia .
Chà, phiên bản thực sự đơn giản hóa là lambda chỉ là cách viết tắt của một hàm ẩn danh. Một ủy viên có thể làm nhiều việc hơn chỉ là các chức năng ẩn danh: những thứ như sự kiện, lệnh gọi không đồng bộ và nhiều chuỗi phương thức.