2012-01-23 27 views
10

Nói rằng tôi có một phương pháp C# nhưCách luồng có thể truy cập biến cục bộ ngay cả sau khi phương thức đã kết thúc?

public void MyMethod() 
    { 
     int i = 0; 

     var thread = new Thread(() => 
     { 
      Thread.Sleep(100); 

      if (i == 0) 
      { 
       Console.WriteLine("Value not changed and is {0}", i); 
      } 
      else 
      { 
       Console.WriteLine(" Value changed to {0}.", i); 
      } 
     }); 

     thread.Start(); 


     i = 1; 
    } 

Ở đây phương pháp này tạo ra một thread mà truy cập vào biến cục bộ được tạo trong phương pháp. Vào thời điểm nó truy cập biến này, phương thức đã hoàn thành và do đó một biến cục bộ tôi không nên tồn tại. Nhưng mã chạy mà không gặp bất kỳ sự cố nào. Theo sự hiểu biết của tôi biến địa phương không tồn tại sau khi khối phương pháp kết thúc. Tôi không thể có được điều này.

Trả lời

4

Theo hiểu biết của tôi, biến cục bộ không tồn tại sau khi khối phương pháp kết thúc.I không thể nhận được điều này.

Hiểu biết của bạn chỉ đơn giản là sai. Đặc tính xác định của biến cục bộ là tên của nó chỉ nằm trong phạm vi bên trong khối khai báo. Đó là lý do tại sao nó được gọi là biến "cục bộ" - bởi vì tên của nó chỉ hiển thị tại địa phương.

Bạn tin rằng giả dối rằng "địa phương" có nghĩa là "sống ngắn". Điều đó là không chính xác; một biến cục bộ ngắn ngủi khi có thể, nhưng có ba tình huống mà nó không thể là: khi nó là biến bên ngoài được đóng kín của một hàm ẩn danh, khi nó nằm trong một khối lặp hoặc khi nó ở trong một khối không đồng bộ.

Thật trùng hợp câu hỏi của bạn là chủ đề của blog của tôi tuần trước; nhìn thấy nó để biết thêm chi tiết:

http://blogs.msdn.com/b/ericlippert/archive/2012/01/16/what-is-the-defining-characteristic-of-a-local-variable.aspx

+0

Thuật ngữ nào bạn sẽ sử dụng để mô tả loại thực thể được sử dụng trong IL để giữ các biến không được nâng lên các trường lớp? Một cái gì đó được viết như một biến nhưng được giữ trong một lớp trình biên dịch là, từ một quan điểm thực hiện, một trường lớp. – supercat

+0

@supercat: Tôi gọi nó là biến cục bộ. Nhưng tôi sẽ nói về ngôn ngữ * MSIL * sau đó, không phải về ngôn ngữ C#. –

+0

Hết sức tò mò, bạn có biết liệu những người tạo C# có được coi là yêu cầu các biến được sử dụng trong một đóng được khai báo một cách khai báo không? Tôi nhận ra rằng các bao đóng có thể hữu ích, nhưng việc ngầm định các biến vào các bao đóng có vẻ nguy hiểm và đôi khi có thể mang lại ngữ nghĩa không rõ ràng. Việc khai báo rõ ràng các bao đóng sẽ làm cho ngữ nghĩa rõ ràng hơn. – supercat

4

Được gọi là closure.
Nó mở rộng các biến địa phương vượt quá tuổi thọ của phương pháp.

15

Điều này hoạt động vì trình biên dịch viết lại mã của bạn để sử dụng closure.

Vì bạn sử dụng biến trong một lambda, biến sẽ kết thúc bằng việc thay đổi thành thành viên của một lớp. Mã được biên dịch không chứa biến cục bộ cho i - ngay cả khi bạn đã viết theo cách đó. Thay vào đó, nó viết lại mã của bạn để sử dụng một lớp trình biên dịch tạo ra có chứa một Int32 như là một biến thành viên, và "mã cục bộ" cũng như lambda tham chiếu đến thành viên lớp này thay thế.

Để biết chi tiết, hãy xem điều này blog post on closures mang đến cho bạn cảm giác thô sơ về những gì trình biên dịch thực hiện ở đây.

0

Biểu thức lambda mà bạn đã khởi chạy dưới dạng chuỗi mới tạo ra một closure trên i; vì các đóng bao đóng trên biến và không phải là giá trị, chuỗi tham chiếu cùng một đối tượng như phạm vi bên ngoài và sẽ có thể truy cập các biến của nó ngay cả sau khi phạm vi bên ngoài đó kết thúc.

0

Một biên dịch C# thường chuyển đổi mã C# để một "ngôn ngữ" trung gian gọi là MSIL đó, như C#, có các biến địa phương. Các biến cục bộ trong MSIL hoạt động như bạn mong đợi các biến C# hoạt động: chúng không tồn tại khi thường trình mà chúng được xác định thoát. Hơn nữa, khi mã C# sử dụng biến cục bộ được dịch sang mã MSIL sử dụng biến cục bộ, các biến C# đó hoạt động giống như biến MSIL: chúng ngừng tồn tại khi hàm xác định thoát. Không phải tất cả mã C# sử dụng biến cục bộ, tuy nhiên, sử dụng biến địa phương MSIL để lưu chúng.Trong các tình huống khác nhau, trình biên dịch sẽ lấy mã được viết để sử dụng biến cục bộ và, trước khi dịch nó thành MSIL, viết lại nó để nó định nghĩa một đối tượng lớp mới có chứa các trường cho các biến được đề cập, và sau đó viết lại truy cập vào các biến đó để chúng thay vào đó truy cập vào các trường mới. Nếu các "biến" được sử dụng bởi một đại biểu, đại biểu sẽ được đưa ra một tham chiếu đến đối tượng lớp mới đó. Ngay cả khi chức năng mà đối tượng đã được xác định thoát, bản thân đối tượng sẽ tiếp tục tồn tại miễn là bất cứ điều gì, bao gồm bất kỳ bản sao nào của đại biểu, giữ một tham chiếu. Các quy tắc xác định khi nào một trình biên dịch sử dụng các biến cục bộ MSIL và khi nó viết lại những thứ để sử dụng các trường lớp có thể phức tạp và có thể thay đổi nhỏ thành một phần của một thói quen để thay đổi ngữ nghĩa của các phần dường như không liên quan khác.

+0

@abelenky: Xin lỗi. – supercat

Các vấn đề liên quan