từ khóa đại biểu so với ký hiệu lambda


Câu trả lời:


140

Câu trả lời ngắn gọn: không.

Câu trả lời dài hơn có thể không liên quan:

  • Nếu bạn chỉ định lambda cho loại đại biểu (chẳng hạn như Funchoặc Action), bạn sẽ có một đại biểu ẩn danh.
  • Nếu bạn gán lambda cho loại Biểu thức, bạn sẽ nhận được cây biểu thức thay vì đại biểu ẩn danh. Cây biểu thức sau đó có thể được biên dịch thành một đại biểu ẩn danh.

Chỉnh sửa: Đây là một số liên kết cho Biểu thức.

  • System.Linq.Expression.Expression (TDelegate) (bắt đầu tại đây).
  • Linq trong bộ nhớ với các đại biểu (như System.Func) sử dụng System.Linq.Enumerable . Linq to SQL (và bất cứ thứ gì khác) với các biểu thức sử dụng System.Linq.Queryable . Kiểm tra các tham số trên các phương pháp đó.
  • Một Giải thích từ ScottGu . Tóm lại, Linq trong bộ nhớ sẽ tạo ra một số phương thức ẩn danh để giải quyết truy vấn của bạn. Linq to SQL sẽ tạo ra một cây biểu thức đại diện cho truy vấn và sau đó dịch cây đó thành T-SQL. Linq to Entities sẽ tạo ra một cây biểu thức đại diện cho truy vấn và sau đó dịch cây đó thành SQL phù hợp với nền tảng.

3
Một loại biểu hiện? Điều này nghe có vẻ như lãnh thổ mới đối với tôi. Tôi có thể tìm hiểu thêm về các loại Biểu thức và sử dụng cây biểu thức trong C # ở đâu?
MojoFilter

2
Thậm chí câu trả lời dài hơn - có nhiều lý do khó hiểu tại sao chúng cũng có thể chuyển đổi thành các loại đại biểu khác nhau :)
Jon Skeet

Lưu ý rằng lambda chỉ có thể được gán cho loại Biểu thức, nếu đó là lambda biểu thức.
Micha Wiedenmann

125

Tôi thích câu trả lời của Amy, nhưng tôi nghĩ tôi là người phạm tội. Câu hỏi cho biết, "Một khi nó được biên dịch" - điều này cho thấy cả hai biểu thức đã được biên dịch. Làm thế nào cả hai có thể biên dịch, nhưng với một được chuyển đổi thành một đại biểu và một thành một cây biểu thức? Đây là một mẹo khó - bạn phải sử dụng một tính năng khác của các phương thức ẩn danh; người duy nhất không được chia sẻ bởi các biểu thức lambda. Nếu bạn chỉ định một phương pháp vô danh mà không chỉ định một danh sách tham số ở tất cả nó là tương thích với bất kỳ loại đại biểu trở về khoảng trống và không có bất kỳ outthông số. Được trang bị kiến ​​thức này, chúng ta sẽ có thể xây dựng hai tình trạng quá tải để làm cho các biểu thức hoàn toàn rõ ràng nhưng rất khác nhau.

Nhưng tai họa ập đến! Ít nhất là với C # 3.0, bạn không thể chuyển đổi biểu thức lambda với thân khối thành biểu thức - bạn cũng không thể chuyển đổi biểu thức lambda bằng một phép gán trong thân (ngay cả khi nó được sử dụng làm giá trị trả về). Điều này có thể thay đổi với C # 4.0 và .NET 4.0, cho phép thể hiện nhiều hơn trong cây biểu thức. Vì vậy, nói cách khác, với các ví dụ MojoFilter đã đưa ra, cả hai sẽ hầu như luôn được chuyển đổi thành cùng một thứ. (Thêm chi tiết trong một phút.)

Chúng ta có thể sử dụng thủ thuật tham số ủy nhiệm nếu chúng ta thay đổi phần thân một chút:

using System;
using System.Linq.Expressions;

public class Test
{
    static void Main()
    {
        int x = 0;
        Foo( () => x );
        Foo( delegate { return x; } );
    }

    static void Foo(Func<int, int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }

    static void Foo(Expression<Func<int>> func)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

Nhưng chờ đã! Chúng ta có thể phân biệt giữa hai loại ngay cả khi không sử dụng cây biểu thức, nếu chúng ta đủ tinh ranh. Ví dụ dưới đây sử dụng quy tắc giải quyết quá tải (và thủ thuật khớp đại biểu ẩn danh) ...

using System;
using System.Linq.Expressions;

public class Base
{
    public void Foo(Action action)
    {
        Console.WriteLine("I suspect the lambda expression...");
    }
}

public class Derived : Base
{
    public void Foo(Action<int> action)
    {
        Console.WriteLine("I suspect the anonymous method...");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        int x = 0;
        d.Foo( () => { x = 0; } );
        d.Foo( delegate { x = 0; } );
    }
}

Ôi. Hãy nhớ rằng trẻ em, mỗi khi bạn quá tải một phương thức được kế thừa từ một lớp cơ sở, một chú mèo con bắt đầu khóc.


9
Tôi lấy bỏng ngô ra và đọc toàn bộ. Đó là một sự khác biệt mà có lẽ tôi sẽ không bao giờ nghĩ đến ngay cả khi tôi đang nhìn thẳng vào mặt nó.
MojoFilter

27
Tôi biết một số điều này, nhưng tôi phải chúc mừng bạn về khả năng của bạn để truyền đạt nó cho con người.
Amy B

1
Đối với bất kỳ ai quan tâm đến những thay đổi trong .NET 4.0 (dựa trên CTP) - xem marcgravell.blogspot.com/2008/11/future-expressions.html . Lưu ý rằng C # 4.0 không làm bất cứ điều gì mới theo như tôi có thể nói.
Marc Gravell

4
Jon, bạn đá. Erik, để trở thành một fanboi Skeet thực thụ, bạn nên đăng ký vào rss stack stack của anh ấy như tôi. Chỉ cần dán stackoverflow.com/users/22656 vào trình đọc nguồn cấp dữ liệu của bạn.
Paul Batum

3
@RoyiNamir: Nếu bạn sử dụng một phương thức ẩn danh không có danh sách tham số, thì điều đó tương thích với bất kỳ loại đại biểu nào với các tham số không tham chiếu / ra, miễn là loại trả về tương thích. Về cơ bản bạn đang nói "Tôi không quan tâm đến các thông số". Lưu ý rằng delegate { ... }không giống như delegate() { ... }- sau này là chỉ tương thích với một loại parameterless đại biểu.
Jon Skeet

2

Trong hai ví dụ trên không có sự khác biệt, không.

Cách diễn đạt:

() => { x = 0 }

là một biểu thức Lambda với phần thân câu lệnh, vì vậy nó không thể được biên dịch thành một cây biểu thức. Trong thực tế, nó thậm chí không biên dịch vì nó cần một dấu chấm phẩy sau 0:

() => { x = 0; } // Lambda statement body
() => x = 0      // Lambda expression body, could be an expression tree. 

6
Chắc chắn điều đó có nghĩa là có một sự khác biệt của "người này sẽ biên dịch, người kia sẽ không";)
Jon Skeet

2

Amy B đúng. Lưu ý rằng có thể có những lợi thế khi sử dụng cây biểu thức. LINQ to SQL sẽ kiểm tra cây biểu thức và chuyển đổi nó thành SQL.

Bạn cũng có thể chơi các thủ thuật với lamdas và cây biểu thức để chuyển hiệu quả tên của các thành viên trong lớp vào một khung theo cách tái cấu trúc an toàn. Moq là một ví dụ về điều này.


-1

Có một sự khác biệt

Thí dụ:

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(delegate
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});   

Và tôi thay thế bằng lambda: (lỗi)

var mytask = Task.Factory.StartNew(() =>
{
    Thread.Sleep(5000);
    return 2712;
});
mytask.ContinueWith(()=>
{
    _backgroundTask.ContinueTask(() =>lblPercent.Content = mytask.Result.ToString(CultureInfo.InvariantCulture));
});

Sai ~ lambda thất bại chỉ vì chữ ký tham số phương thức không khớp.
Jack Wang

-1

Một số điều cơ bản ở đây.

Đây là một phương pháp ẩn danh

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

Vì các phương thức ẩn danh không có tên, chúng ta cần một đại biểu trong đó chúng ta có thể gán cả hai phương thức hoặc biểu thức này. ví dụ

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 ta cần một đại biểu để 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);
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.