2016-02-16 14 views
5

Tôi có một câu hỏi nhanh về hy vọng về các loại Action và Lambdas trong C#. Đây là mã:Tham chiếu đối tượng C# và các loại Hành động

static void Main(string[] args) 
    { 
     List<Action> actions = new List<Action>(); 

     for (int I = 0; I < 10; I++) 
      actions.Add(new Action(() => Print(I.ToString()))); 

     foreach (Action a in actions) 
     { 
      a.Invoke(); 
     } 
     actions.Clear(); 

     int X; 
     for (X = 0; X < 10; X++) 
     { 
      int V = X; 
      actions.Add(new Action(() => Print(V.ToString()))); 
     } 

     foreach (Action a in actions) 
     { 
      a.Invoke(); 
     } 
     Console.ReadLine(); 
    } 


    public static void Print(string s) 
    { 
     Console.WriteLine(s); 
    } 

Nếu bạn chạy mã này, bạn sẽ thấy nó xuất ra 10, mười lần liên tiếp, sau đó xuất số 0-9 lần thứ hai. Nó rõ ràng có một cái gì đó để làm với cách tôi sử dụng X vs tôi, và làm thế nào tôi cho hành động của tôi một biến V mới mỗi lần trong vòng lặp thứ hai ... Có thể mỗi V mới là một địa chỉ mới trong bộ nhớ, nhưng tôi đấu tranh để hiểu lý do tại sao I.ToString() không làm điều tương tự trong vòng lặp đầu tiên ... Tại sao không I.ToString() được sử dụng trong tác vụ Hành động đầu tiên giống như ví dụ thứ hai?

+1

Điều này có thể hữu ích: http://stackoverflow.com/questions/3168375/using-the-iterator-variable-of-foreach-loop-in-a-lambda-expression-why-fails –

Trả lời

5

Các for vòng lặp được hiệu quả mở rộng ra đến này bởi trình biên dịch:

{ 
    int I; 
    for (I = 0; I < 10; I++) 
    { 
    actions.Add(new Action(() => Print(I.ToString()))); 
    } 
} 

Điều này có nghĩa rằng tất cả các trường hợp lambda chụp cùng một ví dụ của I, mà sẽ là 10 khi thoát vòng lặp.

Trong ví dụ thứ hai của bạn, bạn sao chép giá trị vào một biến được phạm vi đến phần thân của câu lệnh for và lambda chụp địa phương này. Sẽ có một địa phương duy nhất cho mỗi sự lặp lại của vòng lặp.

Điều quan trọng là nhận ra rằng bạn không nắm bắt được giá trị của biến, thay vào đó bạn nắm bắt chính biến đó. Đó là lý do tại sao ví dụ đầu tiên không hoạt động, nhưng ví dụ thứ hai thì không.

+0

Cảm ơn lời giải thích. Tôi đã có thể nghĩ rằng I.ToString() đã tạo ra một biến mới, nhưng rõ ràng đó không phải là trường hợp. Giống như hàm gọi hàm I.ToString() được lưu trữ và giá trị của tôi có thể thay đổi và không được đánh giá cho đến khi gọi. –

2

Đó là vì nó chỉ là một đại biểu và nó được thực hiện khi bạn thực sự gọi nó, và tại thời điểm nó được gọi tất cả các hành động có giá trị cuối cùng được đặt cho i, trong trường hợp vòng lặp foreach nó làm cho địa phương bản sao có giá trị như vậy mỗi hành động có giá trị riêng của nó mà làm cho nó in 0- 9.

trong ví dụ đầu tiên giá trị i được đánh giá khi bạn gọi các đại biểu lần đầu tiên trong vòng lặp foreach và tại thời điểm đó i có trong nó, và trong ví dụ thứ hai bạn đang lưu trữ giá trị trong một địa phương bắt chước cùng một hành vi được thực hiện bởi vòng lặp foreach, như vòng lặp foreach cũng làm cho một bản sao cục bộ của giá trị.

bạn cũng có thể đọc this explanation of Jon Skeet và ở đó anh ấy liên kết với 2 bài đăng của lippert eric sẽ giúp bạn nhiều hơn.

+0

Điều này là tốt hơn nhiều giải thích hơn câu trả lời được chấp nhận. – DGibbs

1

Khi Action s được tạo ra trong vòng đầu tiên của bạn, những gì đang thực sự tiết kiệm được cuộc gọi đến phương pháp Print() và giá trị của biến I không thu được cho đến khi cuộc gọi đến Invoke() phương pháp được thực hiện, nhưng điều này xảy ra sau khi vòng lặp đã kết thúc và biến số I có giá trị là 10.

Trong trường hợp thứ hai, bạn đang tạo biến mới V trên mỗi lần lặp lại, để khi bạn thực hiện các hành động được gọi với giá trị của biến V đã được tạo trong lần lặp lại đó.

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