2008-11-05 4 views
140

Tiêu đề không rõ ràng. Những gì tôi muốn biết là nếu điều này là có thể:Vượt qua một System.Type được khởi tạo làm thông số loại cho lớp chung

string typeName = <read type name from somwhere>; 
Type myType = Type.GetType(typeName); 

MyGenericClass<myType> myGenericClass = new MyGenericClass<myType>(); 

Rõ ràng, MyGenericClass được mô tả như sau:

public class MyGenericClass<T> 

Ngay bây giờ, trình biên dịch phàn nàn rằng 'Các loại hoặc không gian tên 'myType' không thể "Phải có cách để làm điều này." Có phải có một cách để làm điều này.

+0

Generics! = Templates. Tất cả các biến kiểu generic được giải quyết tại thời gian biên dịch và không phải lúc chạy. Đây là một trong những tình huống mà loại 'động' của 4.0 có thể hữu ích. – Will

+1

@Will - theo cách nào? Khi được sử dụng với generics, theo CTP hiện tại bạn về cơ bản cuối cùng gọi các phiên bản (trừ khi tôi đang thiếu một thủ thuật ...) –

+0

@MarcGravell bạn có thể sử dụng 'foo.Method ((dynamic) myGenericClass)' cho phương thức thời gian chạy ràng buộc, hiệu quả mô hình định vị dịch vụ cho quá tải phương thức của một loại. –

Trả lời

173

Bạn không thể thực hiện việc này mà không phản ánh. Tuy nhiên, bạn có thể làm điều đó với sự phản ánh. Dưới đây là một ví dụ hoàn chỉnh:

using System; 
using System.Reflection; 

public class Generic<T> 
{ 
    public Generic() 
    { 
     Console.WriteLine("T={0}", typeof(T)); 
    } 
} 

class Test 
{ 
    static void Main() 
    { 
     string typeName = "System.String"; 
     Type typeArgument = Type.GetType(typeName); 

     Type genericClass = typeof(Generic<>); 
     // MakeGenericType is badly named 
     Type constructedClass = genericClass.MakeGenericType(typeArgument); 

     object created = Activator.CreateInstance(constructedClass); 
    } 
} 

Lưu ý: nếu lớp generic của bạn chấp nhận nhiều loại, bạn phải bao gồm dấu phẩy khi bạn bỏ qua các tên kiểu, ví dụ:

Type genericClass = typeof(IReadOnlyDictionary<,>); 
Type constructedClass = genericClass.MakeGenericType(typeArgument1, typeArgument2); 
+0

OK, điều này là tốt, nhưng làm thế nào để đi về phương pháp gọi điện thoại trên tạo ra? Thêm phản ánh? –

+3

Vâng, nếu bạn có thể thoát khỏi việc làm cho loại chung của bạn triển khai giao diện không chung chung, bạn có thể truyền tới giao diện đó. Ngoài ra, bạn có thể viết phương pháp chung của riêng bạn mà tất cả các công việc bạn muốn làm với chung chung, và gọi * rằng * với sự phản ánh. –

+0

(Sau đó bạn sẽ không phải gọi MakeGenericType vv, vì việc xây dựng thể hiện của kiểu generic cũng sẽ được thực hiện bên trong phương thức chung.) –

14

Thật không may là không có. Đối số chung phải được giải quyết tại thời gian biên dịch là 1) một loại hợp lệ hoặc 2) một tham số chung khác. để tạo ra các trường hợp chung dựa trên các giá trị thời gian chạy mà không có búa lớn sử dụng sự phản chiếu.

1

Một số thêm làm thế nào để chạy với kéo mã. Giả sử bạn có một lớp học tương tự như

public class Encoder() { 
public void Markdown(IEnumerable<FooContent> contents) { do magic } 
public void Markdown(IEnumerable<BarContent> contents) { do magic2 } 
} 

Giả sử khi chạy bạn có một FooContent

Nếu bạn đã có thể để ràng buộc tại thời gian biên dịch bạn muốn

var fooContents = new List<FooContent>(fooContent) 
new Encoder().Markdown(fooContents) 

Tuy nhiên bạn không thể thực hiện việc này khi chạy. Để làm điều này khi chạy bạn sẽ làm gì dọc theo dòng:

var listType = typeof(List<>).MakeGenericType(myType); 
var dynamicList = Activator.CreateInstance(listType); 
((IList)dynamicList).Add(fooContent); 

để tự động gọi Markdown(IEnumerable<FooContent> contents)

new Encoder().Markdown((dynamic) dynamicList) 

Lưu ý việc sử dụng dynamic trong lời gọi phương thức. Khi thời gian chạy dynamicList sẽ là List<FooContent> (ngoài ra cũng là IEnumerable<FooContent>) vì ngay cả việc sử dụng động vẫn bắt nguồn từ ngôn ngữ được nhập mạnh, trình kết nối thời gian chạy sẽ chọn phương thức thích hợp Markdown. Nếu không có kết quả khớp chính xác, nó sẽ tìm kiếm một phương thức tham số đối tượng và nếu không khớp với một ngoại lệ kết nối thời gian chạy sẽ được cảnh báo rằng không có phương thức nào phù hợp.

Sự rút ngắn rõ ràng về phương pháp này là mất mát lớn về an toàn loại lúc biên dịch. Tuy nhiên, mã theo các dòng này sẽ cho phép bạn vận hành theo nghĩa rất năng động khi thời gian chạy vẫn được gõ đầy đủ như bạn mong đợi.

2

Yêu cầu của tôi hơi khác một chút, nhưng hy vọng sẽ giúp ai đó. Tôi cần đọc kiểu từ cấu hình và khởi tạo kiểu chung một cách động.

namespace GenericTest 
{ 
    public class Item 
    { 
    } 
} 

namespace GenericTest 
{ 
    public class GenericClass<T> 
    { 
    } 
} 

Cuối cùng, đây là cách bạn gọi nó. Define the type with a backtick.

var t = Type.GetType("GenericTest.GenericClass`1[[GenericTest.Item, GenericTest]], GenericTest"); 
var a = Activator.CreateInstance(t);