2016-04-01 14 views
11

Tại sao tôi không thể gọi SomeGenericMethod<SomeGenericType<>>?Làm cách nào để chuyển loại chung cho một phương pháp chung?

class NotGeneric { } 

class Generic<T> { } 

class Program 
{ 
    static void Main(string[] args) 
    { 
     PrintType(typeof(NotGeneric)); 
     PrintType(typeof(Generic<>)); 
     PrintType<NotGeneric>(); 
     PrintType<Generic<>>(); // compiler goes crazy here 
    } 

    static void PrintType<T>() 
    { 
     Console.WriteLine(typeof(T)); 
    } 

    static void PrintType(Type t) 
    { 
     Console.WriteLine(t); 
    } 
} 
+0

Về cơ bản 'typeof' có thể lấy một tên lớp chung mà không có kiểu generic được xác định, nhưng nếu bạn chuyển kiểu như kiểu chung thì nó phải là kiểu được xác định hoàn toàn mà' Generic <> 'không phải. – juharr

+1

@juharr Như thường lệ ở đây trong SO, câu trả lời tốt nhất cho đến nay là trong các ý kiến. Vì vậy, nó là an toàn để nói rằng là ** không thể ** để làm như vậy? – talles

+0

Tôi nghĩ rbaghbanli đang nói cùng một điều. Nhưng không có bạn không thể vượt qua 'Generic <>' như một loại chung chung. Lưu ý nếu bạn làm 'typeof (Generic )' 'Loại' bạn nhận được tương tự như' typeof (Generic <>) 'nhưng' GenericTypeArguments' sẽ không là một mảng trống, trong khi 'GenericTypeParameters' sẽ là. 'typeof' chỉ cung cấp cho bạn một đối tượng mô tả kiểu bạn đã truyền cho nó. – juharr

Trả lời

2

Tại sao tôi không thể gọi SomeGenericMethod<SomeGenericType<>>

Câu trả lời đơn giản nhất là: vì language specification nói như vậy:

4,4 loại Xây dựng

Một tuyên bố kiểu generic, bởi chính nó, biểu thị một loại chung chung không bị ràng buộc đượ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 này của việc áp dụng các đối số kiểu. Các đối số kiểu được viết trong dấu ngoặc nhọn (< và>) ngay sau tên của loại chung. Một loại bao gồm ít nhất một đối số kiểu được gọi là kiểu được xây dựng. Kiểu được xây dựng có thể được sử dụng ở hầu hết các địa điểm bằng ngôn ngữ mà tên loại có thể xuất hiện. Loại chung chung không liên kết chỉ có thể được sử dụng trong một biểu thức typeof (§7.6.11).

4.4.3 ràng buộc và không ràng buộc kiểu

Các loại ràng buộc hạn đề cập đến một loại không chung hoặc một loại generic không ràng buộc. Loại giới hạn thuật ngữ đề cập đến một loại không chung chung hoặc một loại được xây dựng. Loại không liên kết đề cập đến đối tượng được khai báo theo tuyên bố loại. Một kiểu generic không liên kết không phải là một kiểu, và không thể được sử dụng như kiểu của một biến, đối số hoặc giá trị trả về, hoặc là một kiểu cơ sở. Cấu trúc duy nhất trong đó một kiểu chung không liên kết có thể được tham chiếu là biểu thức typeof (§7.6.11).

Tại sao thông số C# nói như vậy? Bởi vì CLI spec mệnh lệnh đó là tốt:

II.9.4 Instantiating kiểu generic

...

CLI không hỗ trợ instantiation một phần của các kiểu generic. Và các loại chung sẽ không xuất hiện ở bất kỳ đâu trong các đốm chữ ký siêu dữ liệu.

Ok, giờ hãy dừng với pháp lý.Câu trả lời thực tế cho câu hỏi của bạn là:

Khi bạn gọi SomeMethod<SomeType>() cho lần đầu tiên, CLR sẽ gọi trình biên dịch JIT, mà sẽ đọc SomeMethod<T>, thay T, và tạo mã thực thi mới đại diện cho SomeMethod<SomeType>.

Khi bạn gọi SomeMethod<SomeOtherType>(), quá trình cần được lặp lại, mã mới sẽ được tạo cho SomeMethod<SomeOtherType>.

Bạn không thể chỉ tạo mã hợp lệ cho loại chung chung không bị ràng buộc như Generic<>. Trong trường hợp chung, JIT không thể biết được mã đại diện nào để tạo ra mà không biết T.

  • Nếu T là một string và phương pháp của bạn khai báo một biến cục bộ kiểu T, JIT sẽ phân bổ không gian ngăn xếp hoặc sử dụng một thanh ghi với kích thước của một con trỏ bản địa.
  • Nếu TGuid, JIT sẽ phải phân bổ không gian ngăn xếp cho giá trị 128 bit cố định. Nó không thể tạo mã nếu nó không biết T là gì.

Do đó, nói chung, bạn không thể tạo mã thực thi cho loại không liên kết. Lý do bạn cho là có thể chỉ làm điều đó cho đoạn mã bạn đã cung cấp, điều này sẽ yêu cầu vỏ đặc biệt và mã gọi điện của bạn sẽ ngừng hoạt động nếu bạn thêm một số T địa phương theo phương thức PrintType. Như generics phải được tái sử dụng trên các hội đồng, mã gọi không thể giả định bất cứ điều gì về phương pháp được gọi là.

+0

Bây giờ đó là một câu trả lời! Vì vậy, cuối cùng nó là bởi vì trình biên dịch JIT không biết những gì để tạo ra cho 'Generic <>' trong cuộc gọi phương thức (đó là biên dịch JIT "lỗi"). – talles

-2

thử một cái gì đó như thế này:

public class Generic<T> 
{ 
    public void PrintType(T param) 
    { 
     Console.WriteLine(param.GetType().Name); 
    } 
} 
+2

Cách này trả lời câu hỏi tại sao 'PrintType (typeof (Generic <>))' hoạt động bằng 'PrintType >();' doesnt '? – juharr

+0

bạn rõ ràng không thể sử dụng 'Generic <>', bạn cần xác định loại, tức là 'Generic ' –

+1

Đúng nhưng OP thực sự hỏi tại sao 'typeof (Generic <>)' hoạt động. – juharr

0
PrintType<Generic<YOU NEED TO PUT TYPE HERE>>(); 
+3

Bạn nên xem câu hỏi là tại sao OK để thực hiện 'typeof (Generic <>)'. – juharr

+0

@juharr xin lỗi, anh ấy có rất nhiều câu hỏi. – skalinkin

7

biên dịch cần loại chuyên dùng để chuyên dụng phương pháp chung chung và Generic<> không phải là chuyên môn, đó là chung chung. Vì vậy, trong khi Generic<> là một loại, nó không phải là chuyên ngành được sử dụng cho chuyên môn của phương pháp chung chung.

+0

Các 'Generic <>' trong 'typeof (Generic <>)' được giải quyết tại thời gian biên dịch, phải không? – talles

+3

Như tôi đã viết, 'Generic <>' là một loại, do đó, không có vấn đề với 'typeof (Generic <>)'. Nó là loại chung. Nhưng các loại chung không thể được sử dụng cho chuyên môn, vì chúng không phải là chuyên ngành. –

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