Không thể sử dụng tham số ref hoặc out trong biểu thức lambda


172

Tại sao bạn không thể sử dụng tham số ref hoặc out trong biểu thức lambda?

Tôi đã gặp lỗi hôm nay và tìm thấy một cách giải quyết nhưng tôi vẫn tò mò tại sao đây là lỗi thời gian biên dịch.

CS1628 : Không thể sử dụng tham số hoặc tham số 'tham số' bên trong một phương thức ẩn danh, biểu thức lambda hoặc biểu thức truy vấn

Đây là một ví dụ đơn giản:

private void Foo()
{
    int value;
    Bar(out value);
}

private void Bar(out int value)
{
    value = 3;
    int[] array = { 1, 2, 3, 4, 5 };
    int newValue = array.Where(a => a == value).First();
}

Đó là về các trình vòng lặp, nhưng phần lớn lý do tương tự trong bài đăng này (cũng bởi Eric Lippert & mdash; rốt cuộc anh ấy thuộc nhóm thiết kế ngôn ngữ) áp dụng cho lambdas: < blog.msdn.com/ericlippert/archive/2009/07/13 /
Lọ

17
Tôi có thể hỏi cách giải quyết mà bạn đã tìm thấy là gì không?
Beatles1692

3
Bạn chỉ có thể khai báo một biến thông thường cục bộ và làm việc với nó, và gán kết quả cho giá trị sau đó ... Thêm một var tempValue = value; và sau đó làm việc với tempValue.
Mã Drunken Monkey

Câu trả lời:


121

Lambdas có sự xuất hiện của việc thay đổi thời gian tồn tại của các biến mà chúng nắm bắt được. Ví dụ, biểu thức lambda sau đây làm cho tham số p1 tồn tại lâu hơn khung phương thức hiện tại vì giá trị của nó có thể được truy cập sau khi khung phương thức không còn trên ngăn xếp

Func<int> Example(int p1) {
  return () => p1;
}

Một thuộc tính khác của các biến được bắt là các thay đổi đối với biến cũng được hiển thị bên ngoài biểu thức lambda. Ví dụ như các bản in sau 42

void Example2(int p1) {
  Action del = () => { p1 = 42; }
  del();
  Console.WriteLine(p1);
}

Hai thuộc tính này tạo ra một tập hợp hiệu ứng nhất định bay vào mặt tham số ref theo các cách sau

  • tham số ref có thể có tuổi thọ cố định. Xem xét chuyển một biến cục bộ làm tham số ref cho hàm.
  • Các tác dụng phụ trong lambda sẽ cần phải được nhìn thấy trên chính tham số ref. Cả trong phương thức và trong người gọi.

Đây là một số thuộc tính không tương thích và là một trong những lý do chúng không được phép trong các biểu thức lambda.


35
Tôi hiểu rằng chúng ta không thể sử dụng refbên trong lambda biểu hiện, nhưng mong muốn sử dụng nó đã không được cung cấp.
zionpi

84

Trong phần mềm này, phương thức ẩn danh được triển khai bằng cách nâng các biến bị bắt (đó là nội dung câu hỏi của bạn) và lưu trữ chúng dưới dạng các trường của lớp tạo trình biên dịch. Không có cách nào để lưu trữ một refhoặc outtham số như một trường. Eric Lippert đã thảo luận về nó trong một mục blog . Lưu ý rằng có một sự khác biệt giữa các biến được bắt và các tham số lambda. Bạn có thể có "tham số chính thức" như sau vì chúng không phải là biến được bắt:

delegate void TestDelegate (out int x);
static void Main(string[] args)
{
    TestDelegate testDel = (out int x) => { x = 10; };
    int p;
    testDel(out p);
    Console.WriteLine(p);
}

69

Bạn có thể nhưng bạn phải xác định rõ ràng tất cả các loại

(a, b, c, ref d) => {...}

Không hợp lệ, tuy nhiên

(int a, int b, int c, ref int d) => {...}

Có giá trị


13
Nó làm; Câu hỏi là tại sao bạn không thể; Câu trả lời là bạn có thể.
Ben Adams

24
Nó không; câu hỏi là tại sao bạn không thể tham chiếu một biến hiện có , đã được xác định refhoặc out, bên trong lambda. Rõ ràng nếu bạn đọc mã ví dụ (thử lại để đọc lại). Câu trả lời được chấp nhận giải thích rõ ràng tại sao. Câu trả lời của bạn là về việc sử dụng refhoặc out tham số cho lambda. Hoàn toàn không trả lời câu hỏi và nói về điều gì khác
edc65

4
@ edc65 là đúng ... điều này không liên quan gì đến chủ đề của câu hỏi, đó là về nội dung của biểu thức lamba (ở bên phải), không phải danh sách tham số của nó (ở bên trái). Thật kỳ lạ khi điều này nhận được 26 lượt upvote.
Jim Balter

6
Nó đã giúp tôi mặc dù. +1 cho điều đó. Cảm ơn
Emad

1
Nhưng tôi vẫn không hiểu tại sao nó lại được thiết kế như thế này. Tại sao tôi phải xác định rõ ràng tất cả các loại? Về mặt ngữ nghĩa tôi không cần. Có phải tôi đang mất một cái gì đó?
joe

5

Vì đây là một trong những kết quả hàng đầu cho "C # lambda ref" trên Google; Tôi cảm thấy tôi cần phải mở rộng các câu trả lời trên. Cú pháp đại biểu ẩn danh cũ hơn (C # 2.0) hoạt động và nó hỗ trợ các chữ ký phức tạp hơn (cũng như các bao đóng). Các đại biểu ẩn danh của Lambda và ít nhất đã chia sẻ việc thực hiện nhận thức trong phần phụ trợ của trình biên dịch (nếu chúng không giống nhau) - và quan trọng nhất, chúng hỗ trợ các bao đóng.

Những gì tôi đã cố gắng làm khi tôi thực hiện tìm kiếm, để thể hiện cú pháp:

public static ScanOperation<TToken> CreateScanOperation(
    PrattTokenDefinition<TNode, TToken, TParser, TSelf> tokenDefinition)
{
    var oldScanOperation = tokenDefinition.ScanOperation; // Closures still work.
    return delegate(string text, ref int position, ref PositionInformation currentPosition)
        {
            var token = oldScanOperation(text, ref position, ref currentPosition);
            if (token == null)
                return null;
            if (tokenDefinition.LeftDenotation != null)
                token._led = tokenDefinition.LeftDenotation(token);
            if (tokenDefinition.NullDenotation != null)
                token._nud = tokenDefinition.NullDenotation(token);
            token.Identifier = tokenDefinition.Identifier;
            token.LeftBindingPower = tokenDefinition.LeftBindingPower;
            token.OnInitialize();
            return token;
        };
}

Chỉ cần lưu ý rằng Lambdas an toàn về mặt thủ tục và toán học (vì quảng cáo giá trị ref được đề cập trước đó): bạn có thể mở một hộp giun. Hãy suy nghĩ cẩn thận khi sử dụng cú pháp này.


3
Tôi nghĩ rằng bạn đã hiểu nhầm câu hỏi. Câu hỏi đặt ra là tại sao lambda không thể truy cập các biến ref / out trong phương thức container của nó, chứ không phải tại sao chính lambda không thể chứa các biến ref / out. AFAIK không có lý do chính đáng cho thứ hai. Hôm nay tôi đã viết một lambda (a, b, c, ref d) => {...}refđược gạch chân màu đỏ với thông báo lỗi "Thông số '4' phải được khai báo bằng từ khóa 'ref'". Khuôn mặt! PS "khuyến mãi giá trị" là gì?
Qwertie

1
@Qwertie Tôi đã làm điều này để làm việc với tham số hóa đầy đủ, có nghĩa là, bao gồm các loại trên a, b, c và d và nó hoạt động. Xem câu trả lời của BenAdams (mặc dù anh ta cũng hiểu nhầm câu hỏi ban đầu).
Ed Bayiates 10/2/2016

@Qwertie Tôi nghĩ rằng tôi chỉ loại bỏ một nửa điểm đó - Tôi nghĩ rằng điểm ban đầu là việc đặt các thông số ref vào một đóng cửa có thể có rủi ro, nhưng sau đó tôi phải nhận ra rằng điều này không xảy ra trong ví dụ mà tôi đã đưa ra (và cũng không làm Tôi biết liệu điều đó thậm chí sẽ biên dịch).
Jonathan Dickinson

Điều này không liên quan gì đến câu hỏi thực sự được hỏi ... hãy xem câu trả lời được chấp nhận và những bình luận dưới câu trả lời từ Ben Adams, người cũng hiểu sai câu hỏi.
Jim Balter

1

Và có lẽ đây?

private void Foo()
{
    int value;
    Bar(out value);
}

private void Bar(out int value)
{
    value = 3;
    int[] array = { 1, 2, 3, 4, 5 };
    var val = value; 
    int newValue = array.Where(a => a == val).First();
}
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.