2012-02-17 37 views
6

C# là khá nit-chọn khi nói đến phạm vi biến. Làm cách nào để có thể chấp nhận mã này:C# biến phạm vi không nhất quán?

class Program 
{ 
    int x = 0; 

    void foo() 
    { 
     int x = 0; 
     x = 1; 

     Console.WriteLine(x); 
    } 
} 

Nếu bạn hỏi tôi, đó là xung đột đặt tên rõ ràng. Tuy nhiên trình biên dịch (VS 2010) chấp nhận nó. Tại sao?

+4

Bạn có thể tham khảo 'x' và' this.x', vì vậy không có vấn đề gì. – Blorgbeard

+0

Thực ra điều đó là nhất quán. nếu bạn chỉ có x = 0 trong hàm void foo() thì nó sẽ nhận biến từ x mà bạn đã định nghĩa trước đó. Bởi vì bạn có int x = 0 trong phương thức bạn có cho tất cả các ý định đã tạo ra một biến mới. Nhưng nếu bạn nhìn vào giá trị của x bên ngoài của phương pháp foo nó sẽ vẫn là 0 bởi vì bạn đang ở trong một phạm vi khác nhau sau đó. – Brian

+0

Nếu bạn không thích có thể có một biến địa phương có cùng tên với một thành viên lớp, thì bạn có thể nhận được ReSharper và [có nó ngăn cản bạn làm điều này] (http://confluence.jetbrains.net/display/ReSharper/Local + biến + ẩn + thành viên). – AakashM

Trả lời

12

Đây không phải là xung đột đặt tên: trong C#, biến cục bộ được ưu tiên hơn các biến mẫu có cùng tên, vì số scope của chúng hẹp hơn.

Khi trình biên dịch phù hợp với một tài liệu tham khảo tên cho một tuyên bố tên, nó sử dụng việc kê khai phù hợp với phạm vi hẹp nhất

Xem tài liệu trên Reference Matching để biết thông tin chi tiết về đề tài này.

0

Không có xung đột đặt tên. Trình biên dịch luôn lấy biến số nearest/ít nhất.

Trong trường hợp này, đó là biến x bạn khai báo trong foo. Mỗi biến có thể được truy cập theo một cách cụ thể, do đó không có xung đột đặt tên.

Nếu bạn muốn truy cập vào x bên ngoài, bạn có thể sử dụng this.x.

0

Bởi vì quy tắc là nếu xung đột tồn tại giữa biến cục bộ và thành viên lớp, biến cục bộ có mức ưu tiên cao hơn.

0

Đây không phải là mơ hồ mà địa phương sẽ là biến được giả định là có hiệu lực trong chức năng của bạn. Nếu bạn cần để có được biến lớp this.x cho phép độ phân giải tên.

3

Thats bình thường.

Trong các nhà xây dựng, tôi thường sử dụng như nhau.

public Person(string name) { 
    this.name = name; 
} 

Khác sẽ không thể khai báo các tham số phương thức được đặt tên như biến thành viên.

18

Quy tắc ẩn tên C# khá phức tạp. Ngôn ngữ cho phép bạn đề cập đến trường hợp, nhưng không cho phép nhiều trường hợp tương tự. Xem

http://ericlippert.com/2009/11/02/simple-names-are-not-so-simple/

để biết một số thông tin về chủ đề phức tạp này.

Để giải quyết câu hỏi cụ thể của bạn: trình biên dịch chắc chắn có thể phát hiện xung đột đó. Trong thực tế, nó không phát hiện mâu thuẫn:

class P 
{ 
    int x; 
    void M() 
    { 
     x = 123; // The author intends "this.x = 123;" 
     int x = 0; 
    } 
} 

C tương đương với chương trình ++ sẽ là hợp pháp C++, bởi vì trong C++ một biến địa phương đi vào phạm vi tại thời điểm kê khai. Trong C#, một biến cục bộ nằm trong phạm vi toàn bộ khối của nó, và sử dụng nó trước khi khai báo của nó là bất hợp pháp. Nếu bạn cố gắng biên soạn chương trình này bạn sẽ có được:

error CS0844: Cannot use local variable 'x' before it is declared. 
The declaration of the local variable hides the field 'P.x'. 

Xem: khai báo địa phương ẩn lĩnh vực.Trình biên dịch biết điều đó. Vậy tại sao trong trường hợp của bạn không phải là lỗi để ẩn trường?

Giả sử vì lý do tranh luận rằng đó phải là lỗi. Đây có phải là lỗi không?

class B 
{ 
    protected int x; 
} 
class D : B 
{ 
    void M() 
    { 
     int x; 
    } 
} 

Trường x là thành viên của D qua thừa kế từ B. Vì vậy, đây cũng phải là một lỗi, phải không?

Bây giờ giả sử bạn có chương trình này được sản xuất bởi Công ty Cổ phần Foo:

class B 
{ 
} 

và chương trình này được sản xuất bởi Công ty Cổ phần Bar:

class D : B 
{ 
    void M() 
    { 
     int x; 
    } 
} 

Đó biên dịch. Bây giờ giả sử Foo Corp cập nhật lớp cơ sở của họ và tàu một phiên bản mới ra với bạn:

class B 
{ 
    protected int x; 
} 

Bạn đang nói với tôi rằng mỗi lớp có nguồn gốc có chứa một biến địa phương tên là x bây giờ sẽ không biên dịch?

Điều đó thật kinh khủng. Chúng ta phải cho phép các biến cục bộ cho các thành viên shadow.

Và nếu chúng ta sẽ cho phép người dân địa phương để bóng thành viên của các lớp cơ sở, nó sẽ có vẻ hết sức kỳ lạ để không cho phép người dân địa phương để bóng thành viên của các lớp học.

+0

Đó là điều khiến tôi khó hiểu. Vì C# không cho phép nhiều xung đột đặt tên mà C/C++ cho phép, tôi mong đợi sự khắt khe này đối với kịch bản trên. – l33t

+1

@NOPslider: Tôi đã thêm một số văn bản giải thích lý do tại sao đó sẽ là một ý tưởng tồi. –

1

C# 4.0 spec nói về phạm vi này trốn qua làm tổ:

3.7.1.1 Ẩn thông qua tổ

Tên trốn qua làm tổ có thể xảy ra như là kết quả của không gian tên làm tổ hoặc các loại trong không gian tên , dưới dạng kết quả của các loại lồng trong lớp hoặc cấu trúc, và do kết quả của thông số và khai báo biến cục bộ. Trong ví dụ

class A { 
    int i = 0; 
    void F() { 
     int i = 1; 
    } 
    void G() { 
     i = 1; 
    } 
} 

trong phương pháp F, biến dụ tôi bị che khuất bởi các địa phương biến i, nhưng bên trong phương thức G, tôi vẫn đề cập đến trường hợp biến.

Khi tên trong phạm vi bên trong ẩn tên trong phạm vi bên ngoài, nó ẩn tất cả các lần xuất hiện quá tải của tên đó.

Trong ví dụ

class Outer { 
    static void F(int i) {} 
    static void F(string s) {} 
    class Inner 
    { 
     void G() { 
      F(1);   // Invokes Outer.Inner.F 
      F("Hello");  // Error 
     } 
     static void F(long l) {} 
    } 
} 

cuộc gọi F (1) gọi F khai báo trong Inner bởi vì tất cả bên ngoài lần xuất hiện của F được ẩn bằng cách khai báo bên trong. Đối với cùng một lý do , cuộc gọi F ("Hello") dẫn đến lỗi biên dịch.