2014-09-01 17 views
5

Nếu bạn có phương thức hoặc loại Foo<T> thì CLR có thể biên dịch nhiều phiên bản cho T. khác nhau. Tôi biết rằng tất cả các loại tham chiếu đều có cùng phiên bản. Làm thế nào nó hoạt động cho các cấu trúc? Mã đôi khi được chia sẻ hoặc không bao giờ được chia sẻ cho các cấu trúc khác nhau? Tôi có thể tưởng tượng rằng mã được chia sẻ cho tất cả các cấu trúc có cùng kích thước, ví dụ.Khi nào mã được chia sẻ cho các phiên bản khác nhau của generics trong CLR?

Tôi quan tâm bởi vì tôi đang tự hỏi về ví dụ sau:

interface IBar 
{ 
    void DoBar(); 
} 

struct Baz : IBar 
{ 
    public void DoBar(){ ... } 
} 

struct Quux : IBar 
{ 
    public void DoBar(){ ... } 
} 

Bây giờ nếu tôi làm như sau:

public void ExecuteBar<T>(T bar) where T:IBar 
{ 
    bar.DoBar(); 
} 

ExecuteBar(new Baz()); 
ExecuteBar(new Quux()); 

này sẽ tạo ra hai phiên bản của ExecuteBar, mỗi một gọi trực tiếp (không ảo) trực tiếp đến Bar.DoBar()Quux.DoBar()? Hoặc là công văn được thực hiện tại thời gian chạy?

Trả lời

4

Không có câu trả lời thẳng cho câu hỏi này, nó phụ thuộc rất nhiều vào jitter bạn sử dụng và loại mã nào có trong phương pháp chung.

Điểm bắt đầu là jitter tạo ra một phương pháp riêng biệt cho từng loại giá trị riêng lẻ. Ngay cả đối với các cấu trúc khác hoàn toàn giống nhau. Một cái gì đó bạn có thể nhìn thấy với trình gỡ rối. Sử dụng Debug + Windows + Disassembly để xem mã máy được tạo. Bước đơn vào một phương thức, sử dụng cửa sổ Debug + Windows + Registers, thanh ghi EIP/RIP cho bạn biết vị trí của phương thức trong bộ nhớ.

Nhưng, các phương pháp chung như thế này vẫn đủ điều kiện để tối ưu hóa nội tuyến. Rất quan trọng để hoàn thiện, nó làm cho toàn bộ phương thức biến mất và mã trong phương thức được tiêm vào phương thức người gọi. Trong trường hợp này, sự khác biệt giữa các phương pháp chung là sẽ biến mất. Đây không phải là điều bạn thường có thể tin cậy để xảy ra cho các phương thức triển khai giao diện. Tuy nhiên, điều này xảy ra đối với mã mẫu của bạn nếu bạn để trống thân phương thức. Với kết quả khác nhau cho x86 và jitter x64.

Bạn chỉ có thể biết những gì bạn nhận được bằng cách xem mã máy được tạo. Hãy chắc chắn rằng bạn cho phép trình tối ưu hóa thực hiện công việc của mình, Công cụ + Tùy chọn, Gỡ lỗi, Chung, bỏ chọn hộp kiểm "Suppress JIT optimization".Và, tất nhiên, hãy đảm bảo rằng bạn không bao giờ phụ thuộc vào câu trả lời chính xác cho câu hỏi này. Các chi tiết thực hiện như thế này có thể thay đổi mà không cần thông báo.

+0

Chính xác. Vì vậy, tôi đoán rằng, ở cấp độ người hỏi đang quan tâm, câu trả lời là: Phương án thay thế trước đây, hai phương pháp khác nhau được tạo, một cho mỗi loại giá trị và công văn không phải là ảo. –

+0

Cảm ơn! @ Jeppe: đúng vậy. Nó có thể là trường hợp mã được tối ưu hóa thêm bằng cách nội tuyến, và điều đó thậm chí còn hiệu quả hơn, nhưng tôi quan tâm đến mô hình hiệu năng cơ sở của các cuộc gọi giao diện trên các cấu trúc có kiểu được chuyển một cách rõ ràng như tham số cho một phương thức hoặc lớp chung . Tôi biết rằng bạn không nên dựa vào hành vi này, nhưng nó có thể phục vụ như là một điểm khởi đầu để tối ưu hóa. Nếu một công văn làm chậm mã, nó có thể đáng để thay đổi một lớp thành một cấu trúc để loại bỏ công văn. – Jules

1

Các định nghĩa chung không được sử dụng trực tiếp. Thay vào đó các kiểu xây dựng được tạo ra khi chạy.

Một cấu trúc sẽ được tạo cho mỗi đối số loại giá trị. Một cấu trúc duy nhất sẽ được tạo cho tất cả các đối số kiểu tham chiếu.

Afaik, các loại khác nhau không chia sẻ cùng một hướng dẫn asm.

0

tôi đặt mã của bạn vào LINQPad và IL tạo ra cho thấy rằng chỉ có một phiên bản của ExcuteBar trong đó kêu gọi IBar.DoBar()

ExecuteBar: 
IL_0000: nop   
IL_0001: ldarga.s 01 
IL_0003: constrained. 01 00 00 1B 
IL_0009: callvirt UserQuery+IBar.DoBar 
IL_000E: nop   
IL_000F: ret 

Phương pháp ExecuteBar2 (Ibar thanh) trông giống như:

ExecuteBar2: 
IL_0000: nop   
IL_0001: ldarg.1  
IL_0002: callvirt UserQuery+IBar.DoBar 
IL_0007: nop   
IL_0008: ret 

Edit:

class C : IBar { 
    public void DoBar(){} 
} 

loại tham khảo được thông qua như là đối số vào cùng một phương pháp (s) (Baz và Quux được đóng hộp)

+0

Ở cấp độ IL, chắc chắn. Nhưng chú ý rằng tiền tố bị hạn chế. Câu hỏi của tôi là những gì CLR làm với điều đó, không phải những gì trình biên dịch C# làm với nó. – Jules

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