Trước hết, câu trả lời của Henk và Olivier là đúng; Tôi muốn giải thích nó theo một cách hơi khác. Cụ thể, tôi muốn giải quyết điểm này mà bạn đã thực hiện. Bạn có bộ câu lệnh sau:
int k = 10;
int c = 30;
k += c += k += c;
Và sau đó bạn kết luận sai rằng điều này sẽ cho kết quả giống như tập hợp các câu lệnh này:
int k = 10;
int c = 30;
k += c;
c += k;
k += c;
Đó là thông tin để xem bạn đã sai như thế nào và làm thế nào để làm điều đó đúng. Cách đúng để phá vỡ nó là như thế này.
Đầu tiên, hãy viết lại dấu + = ngoài cùng
k = k + (c += k += c);
Thứ hai, viết lại dấu + ngoài cùng. Tôi hy vọng bạn đồng ý rằng x = y + z phải luôn giống như "đánh giá y thành tạm thời, đánh giá z thành tạm thời, tính tổng các thời gian tạm thời, gán tổng cho x" . Vì vậy, hãy làm cho điều đó thật rõ ràng:
int t1 = k;
int t2 = (c += k += c);
k = t1 + t2;
Hãy chắc chắn rằng điều đó rõ ràng, bởi vì đây là bước bạn đã sai . Khi chia nhỏ các thao tác phức tạp thành thao tác đơn giản hơn, bạn phải đảm bảo rằng mình thực hiện từ từ, cẩn thận và không bỏ qua các bước . Bỏ qua các bước là nơi chúng ta mắc sai lầm.
OK, bây giờ hãy chia nhỏ bài tập sang t2, một lần nữa, chậm rãi và cẩn thận.
int t1 = k;
int t2 = (c = c + (k += c));
k = t1 + t2;
Phép gán sẽ gán cùng một giá trị cho t2 như được gán cho c, vì vậy giả sử rằng:
int t1 = k;
int t2 = c + (k += c);
c = t2;
k = t1 + t2;
Tuyệt quá. Bây giờ hãy chia nhỏ dòng thứ hai:
int t1 = k;
int t3 = c;
int t4 = (k += c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
Tuyệt vời, chúng tôi đang tiến bộ. Chia nhỏ nhiệm vụ sang t4:
int t1 = k;
int t3 = c;
int t4 = (k = k + c);
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
Bây giờ hãy chia nhỏ dòng thứ ba:
int t1 = k;
int t3 = c;
int t4 = k + c;
k = t4;
int t2 = t3 + t4;
c = t2;
k = t1 + t2;
Và bây giờ chúng ta có thể xem xét toàn bộ:
int k = 10; // 10
int c = 30; // 30
int t1 = k; // 10
int t3 = c; // 30
int t4 = k + c; // 40
k = t4; // 40
int t2 = t3 + t4; // 70
c = t2; // 70
k = t1 + t2; // 80
Vì vậy, khi chúng ta hoàn thành, k là 80 và c là 70.
Bây giờ chúng ta hãy xem cách điều này được thực hiện trong IL:
int t1 = k;
int t3 = c;
is implemented as
ldloc.0 // stack slot 1 is t1
ldloc.1 // stack slot 2 is t3
Bây giờ điều này là một chút khó khăn:
int t4 = k + c;
k = t4;
is implemented as
ldloc.0 // load k
ldloc.1 // load c
add // sum them to stack slot 3
dup // t4 is stack slot 3, and is now equal to the sum
stloc.0 // k is now also equal to the sum
Chúng tôi có thể đã thực hiện những điều trên như
ldloc.0 // load k
ldloc.1 // load c
add // sum them
stloc.0 // k is now equal to the sum
ldloc.0 // t4 is now equal to k
nhưng chúng tôi sử dụng thủ thuật "lặp lại" bởi vì nó làm cho mã ngắn hơn và làm cho nó dễ bị chập chờn hơn, và chúng tôi nhận được kết quả tương tự. Nói chung, trình tạo mã C # cố gắng giữ các khoảng thời gian tạm thời "phù du" trên ngăn xếp càng nhiều càng tốt. Nếu bạn thấy việc làm theo IL dễ dàng hơn với ít khoảng thời gian hơn, hãy tắt tối ưu hóa và trình tạo mã sẽ ít hoạt động hơn.
Bây giờ chúng ta phải làm cùng một thủ thuật để có được c:
int t2 = t3 + t4; // 70
c = t2; // 70
is implemented as:
add // t3 and t4 are the top of the stack.
dup
stloc.1 // again, we do the dup trick to get the sum in
// both c and t2, which is stack slot 2.
và cuối cùng:
k = t1 + t2;
is implemented as
add // stack slots 1 and 2 are t1 and t2.
stloc.0 // Store the sum to k.
Vì chúng tôi không cần tổng cho bất kỳ điều gì khác, chúng tôi không trùng lặp nó. Ngăn xếp hiện đang trống và chúng ta đang ở cuối câu lệnh.
Đạo lý của câu chuyện là: khi bạn đang cố gắng hiểu một chương trình phức tạp, hãy luôn chia nhỏ các thao tác tại một thời điểm . Đừng đi đường tắt; chúng sẽ khiến bạn lạc lối.