2010-04-22 26 views
12
for (int i = 0; i < 10; i++) 
{ 
    Foo(); 
} 
int i = 10; // error, 'i' already exists 

----------------------------------------  

for (int i = 0; i < 10; i++) 
{ 
    Foo(); 
} 
i = 10; // error, 'i' doesn't exist 

Theo hiểu biết của tôi về phạm vi, ví dụ đầu tiên sẽ ổn. Thực tế không ai trong số họ được cho phép có vẻ kỳ quặc hơn. Chắc chắn 'tôi' là trong phạm vi hay không.phạm vi biến trong các khối câu lệnh

Có điều gì không rõ ràng về phạm vi tôi không hiểu có nghĩa là trình biên dịch thực sự không thể giải quyết vấn đề này? Hoặc chỉ là một trường hợp của trình biên dịch vú-nhà nước?

+0

Nó giống như luật dây an toàn. Không có lý do kỹ thuật nào khiến bạn không thể lái xe mà không có ai, nhưng đó là một công việc tốt luật tồn tại để ngăn bạn, vì nó có thể cứu bạn khỏi một số va chạm rất khó chịu –

Trả lời

16

Bằng sự hiểu biết của tôi về phạm vi, ví dụ đầu tiên nên được tốt.

Hiểu biết về phạm vi của bạn là tốt. Đây không phải là lỗi phạm vi. Việc sử dụng tên lỗi đơn giản không phù hợp.

int i = 10; // lỗi, 'i' đã tồn tại

Đó không phải lỗi được báo cáo. Lỗi được báo cáo là "biến cục bộ có tên i không thể được khai báo trong phạm vi này vì nó sẽ đưa ra một ý nghĩa khác với i đã được sử dụng trong phạm vi con để biểu thị cái gì khác"

Thông báo lỗi là nói cho bạn biết lỗi là gì; đọc lại thông báo lỗi. Nó hư không nói rằng có một xung đột giữa các tuyên bố; nó nói rằng lỗi là vì nó thay đổi ý nghĩa của tên đơn giản. Lỗi là không việc xác nhận lại; nó là hoàn toàn hợp pháp để có hai điều trong hai phạm vi khác nhau có cùng tên, ngay cả khi những phạm vi làm tổ. không phải là hợp pháp là phải có một tên đơn giản nghĩa là hai thứ khác nhau trong các khai báo biến cục bộ lồng nhau.

Bạn sẽ nhận được lỗi "một biến địa phương đặt tên tôi đã được xác định trong phạm vi này" nếu thay vào đó bạn đã làm một cái gì đó giống như

int i = 10; 
int i = 10; 

Chắc chắn 'i' là một trong hai trong phạm vi hay không.

Chắc chắn - nhưng vậy thì sao? Liệu một i nhất định có nằm trong phạm vi hay không là không liên quan. Ví dụ:

class C 
{ 
    int i; 
    void M() 
    { 
     string i; 

Hoàn toàn hợp pháp. Các i bên ngoài là trong phạm vi trong suốt M. Không có vấn đề gì cả với tuyên bố một i địa phương mà bóng tối phạm vi bên ngoài. Điều gì sẽ là một vấn đề nếu bạn nói

class C 
{ 
    int i; 
    void M() 
    { 
     int x = i; 
     foreach(char i in ... 

Vì bây giờ bạn đã sử dụng i để có nghĩa là hai thứ khác nhau trong hai không gian khai báo biến cục bộ lồng nhau - một biến vòng lặp và một trường. Đó là khó hiểu và dễ bị lỗi, vì vậy chúng tôi làm cho nó bất hợp pháp.

Có điều gì không rõ ràng về phạm vi tôi không hiểu có nghĩa là trình biên dịch thực sự không thể giải quyết vấn đề này?

Tôi không hiểu câu hỏi. Rõ ràng là trình biên dịch có thể phân tích hoàn toàn chương trình; nếu trình biên dịch không thể giải quyết ý nghĩa của mỗi lần sử dụng i thì làm thế nào nó có thể báo cáo thông báo lỗi? Trình biên dịch hoàn toàn có thể xác định rằng bạn đã sử dụng 'i' có nghĩa là hai thứ khác nhau trong cùng một không gian khai báo biến cục bộ và báo cáo lỗi tương ứng.

0

Hoặc chỉ là trường hợp của trạng thái biên dịch ?

Chính xác điều đó. Không có ý nghĩa trong việc "tái sử dụng" tên biến trong cùng một phương pháp. Nó chỉ là một nguồn lỗi và không có gì hơn.

+0

Vấn đề không phải là sử dụng lại tên biến; nó là hoàn toàn hợp pháp để tái sử dụng một tên biến nhiều lần trong một phương pháp. Nó không phải là hợp pháp để sử dụng cùng một * tên đơn giản * để chỉ * hai thứ khác nhau * trong một không gian khai báo biến cục bộ cụ thể. –

1

Trong ví dụ đầu tiên, khai báo i bên ngoài vòng lặp làm cho tôi trở thành biến cục bộ của hàm. Kết quả là, có lỗi khi có một tên biến khác mà tôi khai báo trong bất kỳ khối nào của hàm đó.

Thứ hai, tôi chỉ nằm trong phạm vi trong vòng lặp. Bên ngoài vòng lặp, tôi không thể truy cập được nữa.

Vì vậy, bạn đã thấy các lỗi, nhưng không có gì sai với việc này

for (int i = 0; i < 10; i++) 
{ 
    // do something 
} 

foreach (Foo foo in foos) 
{ 
    int i = 42; 
    // do something 
} 

Bởi vì phạm vi của tôi chỉ giới hạn trong phạm vi mỗi khối là.

9

Đó là vì không gian khai báo xác định i ở cấp phương thức. Biến số i nằm ngoài phạm vi kết thúc của vòng lặp, nhưng bạn vẫn không thể redeclare i, bởi vì i đã được xác định trong phương thức đó.

Scope vs Tuyên bố Space:

http://csharpfeeds.com/post/11730/Whats_The_Difference_Part_Two_Scope_vs_Declaration_Space_vs_Lifetime.aspx

Bạn sẽ muốn có một cái nhìn tại câu trả lời Eric Lippert của (người theo mặc định là luôn luôn đúng liên quan đến câu hỏi như thế này).

http://blogs.msdn.com/ericlippert/archive/2009/08/03/what-s-the-difference-part-two-scope-vs-declaration-space-vs-lifetime.aspx

Dưới đây là một lời nhận xét từ eric trên bài đề cập ở trên mà tôi nghĩ rằng các cuộc đàm phán về việc tại sao họ đã làm những gì họ đã làm:

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

+1

Nhưng tại sao bạn có thể xác định 'i' trong phạm vi của một vòng lặp khác trong cùng một phương thức? Tôi vẫn nghĩ đó chỉ là quan liêu trên một phần của trình biên dịch. –

+1

@Michael: Bạn có thể làm điều đó bởi vì các vòng là phạm vi anh chị em. Không có xung đột xác định các biến được đặt tên giống hệt nhau trong các phạm vi anh chị em vì chúng nằm ngoài phạm vi của nhau. –

+0

@Zach: Tuy nhiên khối đầu tiên trong câu hỏi ban đầu cho thấy chính xác rằng: Không có xung đột. Nhưng nó vẫn không biên dịch. – Foxfire

0

nhớ nghĩ rằng trình biên dịch có nghĩa là để nói rằng i đã được tuyên bố ở cấp phương pháp & scoped để trong for vòng lặp.

Vì vậy, trong trường hợp 1 - bạn nhận được một lỗi rằng biến đã tồn tại, mà nó

& trong trường hợp 2 - vì biến có phạm vi chỉ trong for vòng lặp, nó không thể được truy cập bên ngoài vòng lặp mà

để tránh điều này, bạn có thể:

var i = 0; 

for(i = 0, i < 10, i++){ 
} 

i = 10; 

nhưng tôi không thể nghĩ ra một trường hợp bạn muốn thực hiện điều này.

HTH

0

bạn cần làm

  int i ; 
      for (i = 0; i < 10; i++) 
      { 

      } 
      i = 10; 
+3

Đó là mã độc ác. – Foxfire

1

Yea, tôi thứ hai "compilerism vú em-nhà nước" nhận xét. Điều thú vị là điều này là ok.

for (int i = 0; i < 10; i++) 
{ 

} 

for (int i = 0; i < 10; i++) 
{ 

} 

và điều này là ok

for (int i = 0; i < 10; i++) 
{ 

} 

for (int j = 0; j < 10; j++) 
{ 
    var i = 12;     
} 

nhưng đây không phải là

for (int i = 0; i < 10; i++) 
{ 
    var x = 2; 
} 

var x = 5; 

mặc dù bạn có thể làm điều này

for (int i = 0; i < 10; i++) 
{ 
    var k = 12; 
} 

for (int i = 0; i < 10; i++) 
{ 
    var k = 13; 
} 

Đó là tất cả một chút không phù hợp.

EDIT

Dựa trên việc trao đổi ý kiến ​​với Eric dưới đây, tôi nghĩ rằng nó có thể hữu ích để hiển thị như thế nào tôi cố gắng để xử lý vòng. Tôi cố gắng soạn các vòng vào phương pháp riêng của họ bất cứ khi nào có thể. Tôi làm điều này bởi vì nó thúc đẩy khả năng đọc.

TRƯỚC

/* 
* doing two different things with the same name is unclear 
*/ 
for (var index = 0; index < people.Count; index++) 
{ 
    people[index].Email = null; 
} 
var index = GetIndexForSomethingElse(); 

SAU

/* 
* Now there is only one meaning for index in this scope 
*/ 
ClearEmailAddressesFor(people); // the method name works like a comment now 
var index = GetIndexForSomethingElse(); 

/* 
* Now index has a single meaning in the scope of this method. 
*/ 
private void ClearEmailAddressesFor(IList<Person> people) 
{ 
    for (var index = 0; index < people.Count; index++) 
    { 
     people[index].Email = null; 
    } 
} 
+1

Nó không phải là không phù hợp. Quy tắc là cùng một tên đơn giản có thể không được sử dụng để biểu thị hai thứ khác nhau * trong toàn bộ không gian khai báo biến cục bộ ngoài cùng, trong đó tên đơn giản được sử dụng trực tiếp *. Vòng lặp for định nghĩa một không gian khai báo biến cục bộ. Tôi nghĩ bạn sẽ thấy rằng quy tắc này được áp dụng nhất quán trong mỗi ví dụ của bạn. –

+0

Nó có thể nhất quán khi bạn phân tích cú pháp ngôn ngữ của đặc điểm kỹ thuật, nhưng từ quan điểm của người tiêu dùng, nó không phù hợp. Nếu một biến nằm ngoài phạm vi và không thể sử dụng được thì không có lý do chính đáng nào khiến tôi không thể tạo một biến khác có cùng tên. –

+0

Có một lý do * rất tốt *. Lý do rất tốt là vì sử dụng cùng một tên đơn giản để chỉ hai thực thể khác nhau trong không gian khai báo chồng chéo là * gây nhầm lẫn * và * dễ bị lỗi * và do đó phải là * bất hợp pháp *. –

5

Từ C# spec on local variable declarations:

Phạm vi của một biến địa phương tuyên bố trong một biến khai báo địa phương là khối trong đó khai báo xảy ra.

Bây giờ, tất nhiên, bạn không thể sử dụng itrước nó được khai báo, nhưng phạm vi các i khai là khối toàn bộ chứa nó:

{ 
    // scope starts here 
    for (int i = 0; i < 10; i++) 
    { 
     Foo(); 
    } 
    int i = 10; 
} 

Biến fori nằm trong một phạm vi con, do đó sự va chạm của các tên biến.

Nếu chúng ta sắp xếp lại vị trí của việc kê khai, va chạm trở nên rõ ràng hơn:

{ 
    int i = 10; 

    // collision with i 
    for (int i = 0; i < 10; i++) 
    { 
     Foo(); 
    } 
} 
+0

thats một minh họa thực sự rõ ràng zach, cảm ơn. – fearofawhackplanet

0
class Test 
{ 
    int i; 
    static int si=9; 

    public Test() 
    { 
     i = 199; 
    } 

    static void main() 
    { 
     for (int i = 0; i < 10; i++) 
     { 
      var x = 2; 
     } 

     { var x = 3; } 

     { // remove outer "{ }" will generate compile error 
      int si = 3; int i = 0; 

      Console.WriteLine(si); 
      Console.WriteLine(Test.si); 
      Console.WriteLine(i); 
      Console.WriteLine((new Test()).i); 
     } 
    } 
}