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?


84

Đây là một tài liệu hơn là một câu hỏi thực sự. Điều này dường như chưa được giải quyết trên SO (trừ khi tôi bỏ lỡ nó), vì vậy đây là:

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

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

Có một cá thể mới của thành viên cho từng lớp cụ thể hay 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 minh bằng mã như sau:

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

Kết quả là gì và hành vi này được ghi lại ở đâu?


4
Câu trả lời ngắn gọn: Có một thể hiện mới cho mỗi lớp thực tế , tức là một đối tượng cho mỗi loại Tđược sử dụng ( Foo<int>Foo<string>đại diện cho hai lớp khác nhau, và mỗi lớp sẽ có một cá thể, nhưng một số ý định Foo<int>sẽ chia sẻ một phiên bản duy nhất member). Để có ví dụ chi tiết hơn, hãy xem: stackoverflow.com/a/38369256/336648
Kjartan

Câu trả lời:


91

Một statictrường đượ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 dòng mã sau:

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

Về nơi tài liệu này được ghi lại, thông tin sau được tìm thấy trong phần 1.6.5 Các trường của Đặc tả Ngôn ngữ C # (cho C # 3):

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

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

Bản thân khai báo kiểu chung biểu thị một kiểu chung không liên kết được sử dụng làm “bản thiết kế” để tạo thành nhiều kiểu khác nhau, bằng cách áp dụng các đối số kiểu.


26
Đây là một gotcha nếu bạn phát triển bằng cả C # và Java. Mặc dù Foo<int>Foo<String>là các kiểu khác nhau trong C #, nhưng chúng là cùng một kiểu trong Java do cách Java xử lý các kiểu chung (thủ thuật xóa kiểu / trình biên dịch).
Powerlord

Điều gì sẽ xảy ra nếu đây là trường hợp: Foo <int> foo1 = new Foo <int> (); foo1.member = 10; Foo <int> foo2 = new Foo <int> (); foo2.member = 20; chuyện gì xảy ra ở đây thế?
Mọi người

@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 kiểu Foo <int>.
Stas Ivanov

1
Điều gì sẽ xảy ra nếu bạn kế thừa một lớp cơ sở không chung chung có một thành viên tĩnh vào một lớp chung dẫn xuất? Điều này có còn đúng không?
Brain2000

@StasIvanov, foo1.member sẽ vẫn là 10 và foo2.member sẽ là 20. Vui lòng xác minh
SHRI

16

Vấn đề ở đây thực sự là "các lớp chung" không phải là các lớp.

Các định nghĩa chung về lớp chỉ là các khuôn mẫu cho các lớp và cho đến khi các 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).

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

Mối quan hệ đó phản ánh mối quan hệ lớp-đối tượ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 mẫu.

PS Có thể khai báo

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

Từ đó, rõ ràng là không thể chia sẻ các thành viên tĩnh, vì T là khác nhau đối với các chuyên ngành khác nhau.


4

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


1
Chà, còn một quy tắc khác mà tôi không thích trên FX Cop.
Matthew Whited

Nó được ghi lại ở đây
NtFreX

3

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


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 thì tất cả các đối tượng MyClass.Number đều giống nhau 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 cùng lớp với Foo <string> nên hai số này không được chia sẻ.

Một ví dụ để hiển thị đ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

-4

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);

sẽ xuất ra 1vì tôi nghĩ rằng, trong quá trình biên dịch, trình biên dịch tạo 1 lớp cho mỗi lớp chung mà bạn sử dụng (trong ví dụ: Foo<int>Foo<string> ).

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

Nhận xét: Tôi nghĩ rằng đó không phải là một thiết kế tốt và cũng không phải là một phương pháp hay để sử dụng loại thuộc tính tĩnh như vậy.


6
Nếu không chắc chắn 100% - đừng bình luận
sll
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.