2009-07-28 65 views
14

Tôi có hai mẫu mã. Việc đầu tiên không biên dịch, nhưng thứ hai không.Sự nhầm lẫn phạm vi biến đổi trong C#

Mã Mẫu 1(không biên dịch)

public void MyMethod(){ 
    int i=10; 

    for(int x=10; x<10; x++) { 
     int i=10; // Point1: compiler reports error 
     var objX = new MyOtherClass(); 
    } 

    var objX = new OtherClassOfMine(); // Point2: compiler reports error 
} 

Tôi hiểu lý do tại sao các trình biên dịch báo cáo một lỗi ở Point1. Nhưng tôi không hiểu tại sao nó báo cáo lỗi tại Point2. Và nếu bạn nói đó là vì tổ chức bên trong MSIL, thì tại sao ví dụ mã thứ hai lại biên dịch?

mẫu Mã 2(biên dịch)

public void MyMethod(){ 

    for(int x=10; x<10; x++) { 
     int i=10; 
     var objX = new MyOtherClass(); 
    } 

    for(int x=10; x<10; x++) { 
     int i=10; 
     var objX = new MyOtherClass(); 
    } 
} 

Nếu các quy tắc đơn giản của phạm vi biến áp dụng trong Bộ luật Mẫu 2, thì tại sao không những quy tắc tương tự áp dụng đối với Bộ luật Mẫu 1?

Trả lời

39

Có hai quy tắc có liên quan ở đây.

Nguyên tắc liên quan đầu tiên là:

Đây là một lỗi cho một không gian khai báo biến địa phương và một địa phương gian khai báo biến lồng nhau để chứa yếu tố có cùng tên.

(Và một câu trả lời trên trang này gọi ra vị trí khác trong đặc điểm kỹ thuật mà chúng ta gọi này ra một lần nữa.)

Điều đó một mình là đủ để làm cho điều này bất hợp pháp, nhưng trong thực tế, một quy tắc thứ hai làm bất hợp pháp này .

Nguyên tắc liên quan thứ hai trong C# là:

Đối với mỗi lần xuất hiện của một trao nhận dạng làm đơn giản tên tuổi trong một biểu thức hoặc declarator, trong khối địa phương không gian khai báo biến, ngay lập tức kèm theo hoặc khối chuyển đổi của sự xuất hiện đó, mọi lần xuất hiện khác cùng số số nhận dạng dưới dạng tên đơn giản trong biểu thức hoặc khai báo trong ngay lập tức kèm theo khối hoặc chuyển đổi- khối phải tham chiếu đến cùng một thực thể . Quy tắc này đảm bảo rằng ý nghĩa của một tên luôn giống nhau trong một khối nhất định, khối chuyển đổi, cho-, foreach- hoặc sử dụng-tuyên bố, hoặc chức năng ẩn danh.

Bạn cũng cần phải biết rằng vòng lặp for được xử lý như thể có "niềng răng vô hình" xung quanh toàn bộ.

Bây giờ chúng ta biết rằng, chúng ta hãy chú thích mã của bạn:

public void MyMethod() 
{ // 1 
    int i=10; // i1 
    { // 2 -- invisible brace 
     for(int x=10; x<10; x++) // x2 
     { // 3 
     int i=10; // i3 
     var objX = new MyOtherClass(); // objX3 
     } // 3 
    } // 2 
    var objX = new OtherClasOfMine(); // objX1 
} // 1 

Bạn có ba "tên đơn giản", i, x và objX. Bạn có năm biến, mà tôi đã gắn nhãn i1, x2, i3, objX3 và objX1.

Khối ngoài cùng có chứa tập quán của i và objX là khối 1. Do đó, trong khối 1, i và objX phải luôn luôn tham chiếu đến cùng một điều. Nhưng họ thì không. Đôi khi tôi đề cập đến i1 và đôi khi nó đề cập đến i3. Tương tự với objX.

x, tuy nhiên, chỉ bao giờ có nghĩa là x2, trong mỗi khối.

Ngoài ra, cả hai biến "i" đều nằm trong không gian khai báo biến cục bộ giống như cả hai biến "objX".

Do đó, chương trình này là lỗi theo nhiều cách.

Trong chương trình thứ hai của bạn:

public void MyMethod() 
{ // 1 
    { // 2 -- invisible 
     for(int x=10; x<10; x++) // x2 
     { // 3 
     int i=10; // i3 
     var objX = new MyOtherClass(); // objX3 
     } //3 
    } // 2 
    { // 4 -- invisible 
     for(int x=10; x<10; x++) // x4 
     { // 5 
     int i=10; // i5 
     var objX = new MyOtherClass(); // objX5 
     } //5 
    } // 4 
} // 1 

Bây giờ bạn có ba tên đơn giản một lần nữa, và sáu biến.

Các khối ngoài cùng đầu tiên chứa việc sử dụng tên đơn giản x là các khối 2 và 4. Trong khối 2, x đề cập đến x2. Trong suốt khối 4, x là x4. Do đó, điều này là hợp pháp. Tương tự với i và objX - chúng được sử dụng trong các khối 3 và 5 và có nghĩa là những thứ khác nhau trong mỗi khối. Nhưng không nơi nào là cùng một tên đơn giản được sử dụng để có nghĩa là hai điều khác nhau trong suốt cùng một khối.

Bây giờ, bạn có thể lưu ý rằng xem xét tất cả các khối 1, x được sử dụng để có nghĩa là cả x2 và x4. Nhưng không có đề cập đến x đó là bên trong khối 1 nhưng cũng không bên trong khối khác. Do đó, chúng tôi không tính việc sử dụng không nhất quán trong khối 1 là có liên quan.

Ngoài ra, không có khoảng trống khai báo trùng lặp theo cách bất hợp pháp.

Vì vậy, điều này là hợp pháp.

+0

Cảm ơn rất mô tả. –

+0

Nó rất dễ dàng để nhớ nó như là một quy tắc ... nhưng một số như thế nào im không thể tiêu hóa tại sao đó là giới hạn, wrt mã đầu tiên và objX: tôi tạo ra một biến trong một khối và khi tôi đi ra khỏi một vòng nó shud được hoàn thành , giống như tôi không thể truy cập nó bên ngoài khối. –

+3

Nhìn vào nó theo cách này. Nó luôn luôn là hợp pháp để di chuyển tuyên bố của một biến UP trong mã nguồn miễn là bạn giữ nó trong cùng một khối, phải không? Nếu chúng tôi đã làm nó theo cách bạn đề nghị, thì đôi khi điều đó sẽ hợp pháp và đôi khi là bất hợp pháp! Nhưng điều chúng tôi thực sự muốn tránh là những gì xảy ra trong C++ - trong C++, đôi khi di chuyển một tuyên bố biến lên thực sự thay đổi các ràng buộc của các tên đơn giản khác! –

6

Bạn được phép sử dụng cùng tên biến trong các phạm vi không trùng lặp. Tuy nhiên, nếu một phạm vi chồng lên nhau, bạn không thể có cùng một biến được khai báo trong cả hai. Lý do là để ngăn bạn vô tình sử dụng tên biến đã được sử dụng trong phạm vi bên trong, giống như bạn đã làm với i trong ví dụ đầu tiên. Nó không thực sự để ngăn chặn các lỗi objX kể từ đó, thừa nhận, không phải là rất khó hiểu, nhưng lỗi là một hệ quả của cách quy tắc được áp dụng. Trình biên dịch xử lý objX như có nguồn gốc trong phạm vi trong đó nó được khai báo cả trước và sau khi tuyên bố của nó, không chỉ sau.

Trong ví dụ thứ hai, hai vòng for có phạm vi độc lập, không chồng chéo, vì vậy bạn được tự do sử dụng lại iobjX trong vòng lặp thứ hai. Đó cũng là lý do bạn có thể sử dụng lại x làm bộ đếm vòng lặp của mình. Rõ ràng, nó sẽ là một hạn chế câm nếu bạn phải tạo nên các tên khác nhau cho mỗi vòng lặp kiểu for(i=1;i<10;++i) trong một hàm.

Trên một lưu ý cá nhân, tôi thấy lỗi này gây phiền nhiễu và thích cách thức C/C++ cho phép bạn làm bất cứ điều gì bạn muốn, nhầm lẫn là damned.

+0

Bạn nên đọc bài đăng "C++ và Pit Of Despair" của Eric Lippert. Nó sẽ giúp giải thích tại sao nhóm thiết kế C# đưa ra quyết định họ đã làm. Tôi đặc biệt thích câu nói này: "khi tôi hỏi các bạn những gì 'điều hiển nhiên' để làm là, các phân số đáng kể của dân số không đồng ý!" –

12

Từ đặc tả ngôn ngữ C# ...

Phạm vi của biến cục bộ được khai báo trong khai báo biến cục bộ là khối trong đó khai báo xảy ra. Đó là lỗi khi đề cập đến biến số cục bộ ở vị trí văn bản trước biến khai báo cục bộ của biến cục bộ. Trong phạm vi của biến cục bộ, đó là lỗi biên dịch để khai báo một biến số địa phương hoặc hằng số khác với tên .

Trong mẫu mã 1, cả i và objX được khai báo trong phạm vi chức năng, vì vậy không có biến nào khác trong bất kỳ khối nào bên trong hàm đó có thể chia sẻ tên với chúng. Trong mẫu mã 2, cả hai objX được khai báo bên trong vòng lặp, nghĩa là chúng không vi phạm quy tắc không khai báo lại các biến cục bộ trong phạm vi bên trong từ một khai báo khác.

-1

bạn không nên gặp phải lỗi biên dịch với mẫu thứ hai. Hãy thử đổi tên biến thành các chữ cái/tên khác nhau và biên dịch lại vì nó có thể là vấn đề khác với mã nhiều khả năng bạn đã bỏ lỡ một dấu ngoặc nhọn và thay đổi phạm vi phạm vi biến.

+0

Ông nêu rõ các mẫu biên dịch thứ hai, nhưng mẫu đầu tiên thì không. Tôi cho rằng đây là lý do tại sao bạn có một downvote cho việc này. – IamBatman