2012-07-11 32 views
10

Tôi đã nghĩ rằng Generics trong C# đã được thực hiện sao cho một lớp/phương thức/những gì bạn có được tạo ra, vào thời gian chạy hoặc biên dịch, khi một kiểu generic mới được sử dụng, tương tự như các mẫu C++ (mà tôi chưa bao giờ thực sự nhìn vào và tôi rất tốt có thể sai, về việc tôi sẵn sàng chấp nhận sự điều chỉnh).C# Generics được triển khai như thế nào?

Nhưng trong mã của tôi, tôi đã đưa ra một phản ví dụ chính xác:

static class Program { 
    static void Main() 
    { 
     Test testVar = new Test(); 

     GenericTest<Test> genericTest = new GenericTest<Test>(); 
     int gen = genericTest.Get(testVar); 

     RegularTest regTest = new RegularTest(); 
     int reg = regTest.Get(testVar); 

     if (gen == ((object)testVar).GetHashCode()) 
     { 
      Console.WriteLine("Got Object's hashcode from GenericTest!"); 
     } 
     if (reg == testVar.GetHashCode()) 
     { 
      Console.WriteLine("Got Test's hashcode from RegularTest!"); 
     } 
    } 

    class Test 
    { 
     public new int GetHashCode() 
     { 
      return 0; 
     } 
    } 

    class GenericTest<T> 
    { 
     public int Get(T obj) 
     { 
      return obj.GetHashCode(); 
     } 
    } 

    class RegularTest 
    { 
     public int Get(Test obj) 
     { 
      return obj.GetHashCode(); 
     } 
    } 
} 

Cả hai của những giao diện điều khiển dòng in.

Tôi biết rằng lý do thực tế điều này xảy ra là cuộc gọi ảo đến Object.GetHashCode() không giải quyết thành Test.GetHashCode() vì phương thức trong Kiểm tra được đánh dấu là mới thay vì ghi đè. Vì vậy, tôi biết nếu tôi sử dụng "ghi đè" thay vì "mới" trên Test.GetHashCode() thì trả về 0 sẽ đa hình đè lên phương thức GetHashCode trong đối tượng và điều này sẽ không đúng, nhưng theo sự hiểu biết của tôi (trước) của C# generics nó sẽ không có vấn đề vì mỗi trường hợp của T sẽ được thay thế bằng Test, và do đó các cuộc gọi phương thức sẽ có tĩnh (hoặc tại thời gian giải quyết chung) đã được giải quyết cho phương pháp "mới".

Vì vậy, câu hỏi của tôi là: Làm thế nào Generics được thực hiện trong C#? Tôi không biết mã bytecode CIL, nhưng tôi biết Java bytecode vì vậy tôi hiểu cách các ngôn ngữ CLI hướng đối tượng hoạt động ở mức thấp. Cảm thấy tự do để giải thích ở cấp độ đó. Là một sang một bên, tôi nghĩ C# generics đã được thực hiện theo cách đó bởi vì tất cả mọi người luôn luôn gọi hệ thống chung trong C# "True Generics", so với hệ thống loại tẩy xóa của Java.

+0

bất kỳ lý do gì để truyền tới đối tượng ở đây 'gen == ((object) testVar) .GetHashCode()'? – AlwaysAProgrammer

+0

Mặc dù nó không trực tiếp trả lời câu hỏi của bạn, http://blogs.msdn.com/b/ericlippert/archive/2012/07/10/when-is-a-cast-not-a-cast.aspx có một số câu hỏi hay thông tin về cách generics được đúc và cách chúng liên quan với nhau trong C#. – devstruck

+0

@Yogendra Thực hiện việc này truy cập phương thức Object.GetHashCode() thay vì phương thức "mới" Test.GetHashCode(). Đó là lý do tại sao nó trả về một giá trị khác (vì nó chạy một phương thức hoàn toàn khác). – Carrotman42

Trả lời

7

Trong GenericTest<T>.Get(T), trình biên dịch C# có đã chọn được object.GetHashCode nên được gọi (hầu như). Không có cách nào điều này sẽ giải quyết cho phương thức "mới" GetHashCode khi chạy (sẽ có khe riêng trong bảng phương thức, thay vì ghi đè vị trí cho object.GetHashCode).

Từ Eric Lippert của What's the difference, part one: Generics are not templates, vấn đề này được giải thích (thiết lập sử dụng là hơi khác nhau, nhưng những bài học dịch tốt với kịch bản của bạn):

này minh họa rằng Generics trong C# không giống như các mẫu trong C++. Bạn có thể xem các mẫu như là một cơ chế tìm kiếm và thay thế của quần ưa thích. [...] Đó không phải là cách các loại chung hoạt động; loại chung là, , chung. Chúng tôi thực hiện quá trình phân giải quá tải sau khi và nướng trong kết quả . [...] IL mà chúng tôi đã tạo cho loại chung đã có phương thức được gọi là chọn. Các jitter không nói "tốt, tôi xảy ra để biết rằng nếu chúng tôi yêu cầu trình biên dịch C# để thực hiện ngay bây giờ với thông tin bổ sung này sau đó nó đã có thể chọn một quá tải khác nhau . Hãy để tôi viết lại mã được tạo ra để bỏ qua mã số mà trình biên dịch C# ban đầu được tạo ra ... ”Người jitter biết không có gì về các quy tắc của C#.

Và một cách giải quyết đối với ngữ nghĩa mong muốn của bạn:

Bây giờ, nếu bạn làm muốn giải quyết tình trạng quá tải phải được tái thực hiện tại thời gian chạy dựa trên các loại thời gian chạy của các đối số, chúng ta có thể làm điều đó cho bạn; đó là tính năng “năng động” mới hoạt động trong C# 4.0. Chỉ cần thay "đối tượng" bằng "động" và khi bạn thực hiện cuộc gọi liên quan đến đối tượng đó, chúng tôi sẽ chạy quá tải thuật toán phân giải khi chạy và tự động nhổ mã gọi phương pháp mà trình biên dịch đã chọn tất cả các loại thời gian chạy lúc biên dịch.

+3

Ah Eric, chúng tôi sẽ làm gì nếu không có bạn. – Servy

+0

Vâng, tôi đã nói rằng trong đoạn bắt đầu với "Tôi biết rằng lý do thực tế điều này xảy ra là ..." Câu hỏi của tôi là "C# Generics được triển khai như thế nào?" Tôi sẽ đọc các liên kết bạn gửi mặc dù, có lẽ đó sẽ trả lời câu hỏi của tôi. – Carrotman42

+0

@ chỉnh sửa của bạn: Nó không phải là tôi muốn làm điều này: Tôi đến từ một nền tảng của Java, nơi toàn bộ thử nghiệm "phương pháp mới" này không tồn tại. Tôi nghĩ rằng nó là khá dễ bị lỗi người dùng và tôi không có kế hoạch bao giờ sử dụng nó vào mục đích. Lý do tôi gặp phải là vì tôi đang viết một lớp trừu tượng mà tôi muốn ép buộc các lớp con triển khai GetHashCode, vì vậy tôi đã viết "public abstract int GetHashcode();", không nhận ra điều đó. gọi GetHashCode một cách tổng quát, tôi đã thực sự nói "public override abstract int GetHashcode();", đó là một cách khủng khiếp đối với sở thích của tôi. – Carrotman42

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