2010-02-28 41 views
6

tôi đã viết phương pháp mở rộng này:Làm thế nào để gọi một phương thức mở rộng chung một cách năng động?

public static DataTable ToDataTable<T>(this IList<T> list) 
{...} 

Nó hoạt động tốt nếu gọi với một loại được biết đến tại thời gian biên dịch:

DataTable tbl = new List<int>().ToDataTable(); 

Nhưng làm thế nào để gọi nó là nếu loại generic không biết?

object list = new List<int>(); 
... 
tbl = Extension.ToDataTable((List<object>)list); // won't work 
+0

Tại sao bạn truyền vào 'Danh sách '? 'Danh sách' của bạn là một' Danh sách ', dàn diễn viên sẽ không thành công. – Vlad

+0

Bởi vì anh ta không biết tại thời gian biên dịch loại danh sách anh ta có: anh ta không biết đó là 'Danh sách '. Anh ấy đang cố gắng thu thập nó bằng cách chuyển sang một lớp cơ sở (như bạn lưu ý đúng, sẽ không hoạt động vì 'List ' không tương thích với 'List ' mặc dù 'int' tương thích với' đối tượng') . – itowlson

Trả lời

9

này xảy ra vì một List<int> không phải là một List<object> - các loại List không hiệp biến trong tham số loại nguyên tố của nó. Đáng tiếc là bạn sẽ cần phải nhận được một phiên bản đánh máy của phương pháp chung chung và gọi nó là sử dụng phản ánh:

Type listItemType = typeof(int); // cheating for simplicity - see below for real approach 
MethodInfo openMethod = typeof(Extension).GetMethod("ToDataTable", ...); 
MethodInfo typedMethod = openMethod.MakeGenericMethod(typeof(listItemType)); 
typedMethod.Invoke(null, new object[] { list }); 

Một thay thế có thể để tạo ra một phiên bản của phương pháp mở rộng của bạn chấp nhận IList hơn IList<T>. Lớp List<T> thực hiện giao diện này không chung chung cũng như giao diện chung, vì vậy bạn sẽ có thể gọi:

public static DataTable WeakToDataTable(this IList list) { ... } 

((IList)list).WeakToDataTable(); 

(Trên thực tế bạn muốn có thể sử dụng một quá tải chứ không phải là một cái tên khác - chỉ cần sử dụng một tên khác nhau để gọi ra các loại khác nhau)


Thông tin thêm:. trong giải pháp phản xạ, tôi bỏ qua vấn đề làm thế nào để xác định loại nguyên tố danh sách. Điều này có thể hơi phức tạp tùy thuộc vào mức độ tinh vi mà bạn muốn nhận. Nếu bạn đang giả định rằng đối tượng sẽ là một List<T> (đối với một số T) sau đó thật dễ dàng:

Type listItemType = list.GetType().GetGenericArguments()[0]; 

Nếu bạn chỉ sẵn sàng để đảm nhận IList<T> thì đó là một chút khó khăn hơn, bởi vì bạn cần phải xác định vị trí thích hợp giao diện và nhận được đối số chung từ đó. Và bạn không thể sử dụng GetInterface() vì bạn đang tìm kiếm một cá thể được xây dựng khép kín của một giao diện chung. Vì vậy, bạn phải lăn qua tất cả các giao diện tìm kiếm một trong đó là một thể hiện của IList<T>:

foreach (Type itf in list.GetType().GetInterfaces()) 
{ 
    if (itf.IsGenericType && itf.GetGenericTypeDefinition == typeof(IList<>)) // note generic type definition syntax 
    { 
    listItemType = itf.GetGenericArguments()[0]; 
    } 
} 

này sẽ làm việc cho các danh sách trống rỗng, vì nó đi ra khỏi siêu dữ liệu, không phải là nội dung danh sách.

+0

Sẽ không chỉ truyền tới 'Danh sách 'thay vì' Danh sách ' giải quyết vấn đề? – Vlad

+0

Chắc chắn, nhưng câu hỏi hỏi "làm thế nào để gọi nó ** nếu loại chung không được biết **?" (nhấn mạnh thêm).Do đó, lưu ý của tôi rằng trong thực tế anh ta cũng phải tìm ra listItemType bằng cách sử dụng sự phản chiếu thay vì giả sử nó là int. – itowlson

+0

Tôi đã thử điều này, nhưng tôi có hai vấn đề: 1. Làm thế nào tôi sẽ nhận được loại phần tử nhúng nếu danh sách trống? 2. Tôi có hai phương thức mở rộng ToDataTable(). Làm thế nào để có được một cho IList ? –

0

Sau khi gặp sự cố để làm việc với giao diện IList<T>, tôi đã giải quyết nó bằng giao diện IList như được đề xuất itowlson. Đó là một chút xấu xí vì phương pháp _T nhưng nó hoạt động tốt:

DataTable tbl = ((IList)value).ToDataTable(); 

public static class Extensions 
{ 
    private static DataTable ToDataTable(Array array) {...} 
    private static DataTable ToDataTable(ArrayList list) {...} 
    private static DataTable ToDataTable_T(IList list) {...} 

    public static DataTable ToDataTable(this IList list) 
    { 
     if (list.GetType().IsArray) 
     { 
      // handle arrays - int[], double[,] etc. 
      return ToDataTable((Array)list); 
     } 
     else if (list.GetType().IsGenericType) 
     { 
      // handle generic lists - List<T> etc. 
      return ToDataTable_T(list); 
     } 
     else 
     { 
      // handle non generic lists - ArrayList etc. 
      return ToDataTable((ArrayList)list); 
     }    
    } 
} 
Các vấn đề liên quan