2010-06-14 40 views
66

Đây là tài liệu nhiều hơn câu hỏi thực. Đây dường như không đã được giải quyết trên SO chưa (trừ khi tôi bị mất nó), vì vậy ở đây đi:Các thành viên tĩnh của một lớp chung có gắn với cá thể cụ thể không?

Hãy tưởng tượng một lớp generic có chứa một thành viên tĩnh:

class Foo<T> { 
    public static int member; 
} 

Có một trường hợp mới của thành viên cho từng lớp cụ thể, hoặc chỉ có một cá thể duy nhất cho tất cả các lớp kiểu Foo?

Nó có thể dễ dàng được xác nhận bởi mã như thế này:

Foo<int>.member = 1; 
Foo<string>.member = 2; 
Console.WriteLine (Foo<int>.member); 

kết quả là gì, và ở đâu là hành vi này ghi nhận?

+3

Câu trả lời ngắn: Có một trường hợp mới cho mỗi lớp * thực tế *, ví dụ một cho mỗi loại 'T' được sử dụng (' Foo 'và' Foo 'đại diện cho hai lớp khác nhau, và sẽ có một ví dụ, nhưng một vài ý định của 'Foo ' sẽ chia sẻ một cá thể duy nhất của 'thành viên'). Để biết ví dụ chi tiết hơn, hãy xem: http://stackoverflow.com/a/38369256/336648 – Kjartan

Trả lời

76

Trường A static được chia sẻ trên tất cả các trường hợp cùng loại. Foo<int>Foo<string> là hai loại khác nhau. Điều này có thể được chứng minh bằng các dòng mã sau đây:

// this prints "False" 
Console.WriteLine(typeof(Foo<int>) == typeof(Foo<string>)); 

Đối với nơi này được ghi chép lại, sau đây được tìm thấy trong phần 1.6.5 Fields của C# Language Specification (đối với C# 3):

Trường tĩnh xác định chính xác một vị trí lưu trữ . Không có vấn đề bao nhiêu trường hợp của một lớp học được tạo ra, chỉ có một bản sao của một trường tĩnh .

Như đã nêu trước đây; Foo<int>Foo<string> không phải là cùng một lớp; chúng là hai lớp khác nhau được xây dựng từ cùng một lớp chung. Làm thế nào điều này xảy ra được nêu trong phần 4.4 của tài liệu đề cập ở trên:

Một khai báo kiểu chung chung, bởi chính nó, biểu thị một kiểu chung chung không ràng buộc rằng được sử dụng như một “kế hoạch chi tiết” để tạo thành nhiều loại khác nhau, bằng cách áp dụng các đối số loại .

+20

Dưới đây là một hình ảnh xác thực nếu bạn phát triển trong cả C# và Java. Trong khi 'Foo ' và 'Foo ' là các kiểu khác nhau trong C#, chúng là kiểu ** ** tương tự trong Java vì cách Java xử lý các generic (thủ thuật xóa/thủ thuật loại). – Powerlord

+0

Nếu đây là trường hợp: Foo foo1 = new Foo (); foo1.thành viên = 10; Foo foo2 = new Foo (); foo2.member = 20; Điều gì xảy ra ở đây? – Everyone

+0

@Mọi giá trị của thành viên sẽ được thay đổi cho cả hai trường hợp (và sẽ là 20) vì chúng có cùng loại Foo . –

4

Chúng không được chia sẻ. Không chắc chắn nơi nó được ghi lại nhưng cảnh báo phân tích CA1000 (Không khai báo thành viên tĩnh theo loại chung) cảnh báo chống lại điều này do có nguy cơ làm cho mã phức tạp hơn.

+1

Chà, một quy tắc khác mà tôi không đồng ý với FX Cop. –

-2

IMO, bạn cần phải kiểm tra nó, nhưng tôi nghĩ rằng

Foo<int>.member = 1; 
Foo<string>.member = 2; 
Console.WriteLine (Foo<int>.member); 

chí đầu ra 1 bởi vì tôi nghĩ rằng, trong thời gian biên soạn, các compilator tạo 1 lớp cho mỗi lớp generic bạn sử dụng (trong bạn ví dụ: Foo<int>Foo<string>).

Nhưng tôi không chắc chắn 100% =).

Lưu ý: Tôi nghĩ đó không phải là một thiết kế hay cũng không phải là cách hay để sử dụng các thuộc tính tĩnh như vậy.

+3

Nếu không chắc chắn 100% - không bình luận – sll

1

Chúng không thực sự được chia sẻ. Bởi vì thành viên không thuộc về cá thể nào cả. Một thành viên lớp tĩnh thuộc về chính lớp đó. Vì vậy, nếu bạn có MyClass.Number nó là như nhau cho tất cả các đối tượng MyClass.Number bởi vì nó thậm chí không phụ thuộc vào đối tượng. Bạn thậm chí có thể gọi hoặc sửa đổi MyClass.Number mà không cần bất kỳ đối tượng nào.

Nhưng vì Foo < int> không phải là cùng một lớp với chuỗi Foo <> hai số này không được chia sẻ.

Một ví dụ cho thấy điều này:

TestClass<string>.Number = 5; 
TestClass<int>.Number = 3; 

Console.WriteLine(TestClass<string>.Number); //prints 5 
Console.WriteLine(TestClass<int>.Number);  //prints 3 
13

Vấn đề ở đây thực sự là một thực tế rằng "các lớp học chung chung" không phải là lớp học ở tất cả.

Các định nghĩa lớp chung chỉ là mẫu cho các lớp và cho đến khi tham số kiểu của chúng được chỉ định, chúng chỉ là một đoạn văn bản (hoặc một số byte).

Khi chạy, người ta có thể chỉ định tham số kiểu cho mẫu, do đó, đưa nó vào cuộc sống và tạo một lớp của loại, bây giờ, được chỉ định đầy đủ. Đó là lý do tại sao các thuộc tính tĩnh không phải là toàn bộ mẫu và đó là lý do tại sao bạn không thể truyền giữa List<string>List<int>.

Loại mối quan hệ đó phản ánh mối quan hệ đối tượng lớp. Cũng giống như các lớp không tồn tại * cho đến khi bạn khởi tạo một đối tượng từ chúng, các lớp chung không tồn tại, cho đến khi bạn tạo một lớp dựa trên khuôn mẫu.

P.S. Hoàn toàn có thể khai báo

class Foo<T> { 
    public static T Member; 
} 

Từ đây rõ ràng là các thành viên tĩnh không thể được chia sẻ, vì T khác nhau cho các chuyên môn khác nhau.

3

C# việc triển khai các generics là gần gũi hơn với C++. Trong cả hai ngôn ngữ này, MyClass<Foo>MyClass<Bar> không chia sẻ các thành viên tĩnh nhưng trong Java chúng thực hiện. Trong C# và C++ MyClass<Foo> nội bộ tạo kiểu hoàn toàn mới tại thời gian biên dịch như thể generics là loại macro. Bạn thường có thể thấy tên được tạo của họ trong theo dõi ngăn xếp, như MyClass'1MyClass'2. Đây là lý do tại sao họ không chia sẻ các biến tĩnh. Trong Java, generics được thực hiện bằng phương thức đơn giản hơn của trình biên dịch tạo ra mã bằng cách sử dụng các kiểu không chung chung và thêm các kiểu phôi trên tất cả. Vì vậy, MyClass<Foo>MyClass<Bar> không tạo hai lớp hoàn toàn mới trong Java, thay vào đó cả hai đều cùng lớp MyClass bên dưới và đó là lý do tại sao chúng chia sẻ các biến tĩnh.

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