2010-08-23 3 views
6

Tôi có một bộ sưu tập IEnumerable mà số lượng chính xác và các loại có thể thay đổi thường xuyên (do tạo mã tự động).Làm thế nào để gọi System.Linq.Enumerable.Count <> trên IEnumerable <T> bằng cách sử dụng Reflection?

Nó trông giống như sau:

public class MyCollections { 
    public System.Collections.Generic.IEnumerable<SomeType> SomeTypeCollection; 
    public System.Collections.Generic.IEnumerable<OtherType> OtherTypeCollection; 
    ... 

Khi chạy tôi muốn xác định từng loại và nó đếm mà không cần phải viết lại mã sau mỗi thế hệ mã. Vì vậy, tôi đang tìm kiếm một cách tiếp cận chung bằng cách sử dụng sự phản chiếu. Kết quả tôi đang tìm kiếm là một cái gì đó như:

MyType: 23 
OtherType: 42 

Vấn đề của tôi là tôi không thể tìm cách gọi phương thức Đếm đúng cách. Dưới đây là những gì tôi có cho đến nay:

 // Handle to the Count method of System.Linq.Enumerable 
     MethodInfo countMethodInfo = typeof(System.Linq.Enumerable).GetMethod("Count", new Type[] { typeof(IEnumerable<>) }); 

     PropertyInfo[] properties = typeof(MyCollections).GetProperties(); 
     foreach (PropertyInfo property in properties) 
     { 
      Type propertyType = property.PropertyType; 
      if (propertyType.IsGenericType) 
      { 
       Type genericType = propertyType.GetGenericTypeDefinition(); 
       if (genericType == typeof(IEnumerable<>)) 
       { 
        // access the collection property 
        object collection = property.GetValue(someInstanceOfMyCollections, null); 

        // access the type of the generic collection 
        Type genericArgument = propertyType.GetGenericArguments()[0]; 

        // make a generic method call for System.Linq.Enumerable.Count<> for the type of this collection 
        MethodInfo localCountMethodInfo = countMethodInfo.MakeGenericMethod(genericArgument); 

        // invoke Count method (this fails) 
        object count = localCountMethodInfo.Invoke(collection, null); 

        System.Diagnostics.Debug.WriteLine("{0}: {1}", genericArgument.Name, count); 
       } 
      } 
     } 
+0

Có phải "MyCollections" là một loại biến/trường/thuộc tính không? Bạn dường như đang sử dụng nó như cả hai. –

+0

Số của bạn MethodInfo-tham chiếu là null. Sửa lỗi đó và mã có thể hoạt động. –

+0

Xin lỗi, ví dụ đã cho không chính xác. Tôi đã sưa nó. – mbi

Trả lời

3

Nếu bạn nhấn mạnh vào làm việc đó một cách khó khăn; p

Thay đổi:

  • làm thế nào bạn có được countMethodInfo cho một generic method
  • biện minh cho khả Gọi

Mã (ghi chú obj là ví dụ của tôi là MyCollections) :

MethodInfo countMethodInfo = typeof (System.Linq.Enumerable).GetMethods().Single(
     method => method.Name == "Count" && method.IsStatic && method.GetParameters().Length == 1); 

    PropertyInfo[] properties = typeof(MyCollections).GetProperties(); 
    foreach (PropertyInfo property in properties) 
    { 
     Type propertyType = property.PropertyType; 
     if (propertyType.IsGenericType) 
     { 
      Type genericType = propertyType.GetGenericTypeDefinition(); 
      if (genericType == typeof(IEnumerable<>)) 
      { 
       // access the collection property 
       object collection = property.GetValue(obj, null); 

       // access the type of the generic collection 
       Type genericArgument = propertyType.GetGenericArguments()[0]; 

       // make a generic method call for System.Linq.Enumerable.Count<> for the type of this collection 
       MethodInfo localCountMethodInfo = countMethodInfo.MakeGenericMethod(genericArgument); 

       // invoke Count method (this fails) 
       object count = localCountMethodInfo.Invoke(null, new object[] {collection}); 

       System.Diagnostics.Debug.WriteLine("{0}: {1}", genericArgument.Name, count); 
      } 
     } 
    } 
+0

Cảm ơn bạn rất nhiều! Sau khi kiểm tra phản ứng của bạn, tôi đã hiểu sai lầm của mình. Trong thực tế, chỉ có các đối số cho Invoke phải được điều chỉnh (tất nhiên!) Và sau đó tất cả việc triển khai ban đầu của tôi hoạt động như mong đợi. Thậm chí khó khăn của bạn tiếp cận Linq để lấy countMethodInfo trông sexy nó cảm thấy một chút để complcated cho tôi ;-) – mbi

+0

@embee - khi tôi đã thử mã ban đầu của bạn, tôi đã nhận 'null' cho methodinfo ... –

+0

nó hoạt động tốt cho silverlight/windows phone – mbi

2

Đó sẽ liên quan đến một số MakeGenericMethod - và rất nhiều phản ánh thường. Cá nhân, tôi sẽ bị cám dỗ để chỉ đơn giản hóa bằng cách ditching generics trong trường hợp này:

public static int Count(IEnumerable data) { 
    ICollection list = data as ICollection; 
    if(list != null) return list.Count; 
    int count = 0; 
    IEnumerator iter = data.GetEnumerator(); 
    using(iter as IDisposable) { 
     while(iter.MoveNext()) count++; 
    } 
    return count; 
} 

Bạn có thể truyền lên phi generic IEnumerable trivially, ngay cả khi lấy thông qua phản ánh.

+0

Tất nhiên, đây là một ý tưởng hay. Nhưng tôi đang tìm giải pháp cho lỗ hổng của tôi trong MakeGenericMethod. – mbi

+0

Chúng ta không thể chỉ là 'foreach' trên 'dữ liệu', hay chúng ta cần phải đối phó với điều tra viên một cách rõ ràng? – AakashM

+0

@embee - 2 giây, tôi sẽ xem ... –

1
var count = System.Linq.Enumerable.Count(theCollection); 

Chỉnh sửa: bạn nói nó được tạo ra mặc dù vậy, bạn không thể tạo thuộc tính có cuộc gọi đến Count()?

public class MyCollections 
{ 
    public System.Collections.Generic.IEnumerable<SomeType> SomeTypeCollection; 
    public System.Collections.Generic.IEnumerable<OtherType> OtherTypeCollection; 

    public int CountSomeTypeCollection 
    { 
     get { return this.SomeTypeCollection.Count(); } 
    } 

    ... 
+0

Xin lỗi, đã bỏ lỡ sự thật là bạn không biết 'T' lúc biên dịch. –

+0

MyCollections được tạo tự động và ghi đè khá thường xuyên. Đây là lý do tại sao tôi phải sử dụng sự phản chiếu từ bên ngoài. Tôi biết nó phức tạp nhưng nó sẽ là có thể ... – mbi

1

Bởi bây giờ, câu hỏi đã được trả lời, nhưng tôi muốn giới thiệu bạn một tỉa xuống — và tôi nghĩ, chứ không phải tầm thường phiên bản — của "gọi một phương pháp khuyến nông chung chung", mà có thể được sử dụng để gọi Count phản tư:

// get Enumerable (which holds the extension methods) 
Type enumerableT = typeof(Enumerable); 

// get the Count-method (there are only two, you can check the parameter-count as in above 
// to be certain. Here we know it's the first, so I use the first: 
MemberInfo member = enumerableT.GetMember("Count")[0]; 

// create the generic method (instead of int, replace with typeof(yourtype) in your code) 
MethodInfo method = ((MethodInfo) member).MakeGenericMethod(typeof(int)); 

// invoke now becomes trivial 
int count = (int)method.Invoke(null, new object[] { yourcollection }); 

các công trình trên, bởi vì bạn không cần phải sử dụng các loại chung của IEnumerable<> để có thể gọi Count, đó là một phần mở rộng của Enumerable và mất một đối số của IEnumerable<T> làm thông số đầu tiên (đó là một phần mở rộng), nhưng bạn không cần phải xác định điều đó. Lưu ý rằng, từ việc đọc câu hỏi của bạn, có vẻ như với tôi rằng bạn thực sự nên sử dụng Generics cho các loại của bạn, điều này cho biết thêm tính an toàn kiểu vào dự án của bạn và vẫn cho phép bạn sử dụng Count hoặc bất kỳ thứ gì. Xét cho cùng, một điều chắc chắn là tất cả đều là Enumerable, phải không? Nếu vậy, ai cần sự phản chiếu?

+0

Lý do phản chiếu rất đơn giản: những gì bạn gắn nhãn là "yourtype" không được biết tại thời gian biên dịch (hoặc ít nhất nó thay đổi thường xuyên). – mbi

+0

@embee: điều đó, tôi hiểu. Nhưng nếu nó thay đổi thường xuyên (nó được tạo ra) nó là lý tưởng cho generics. Nhưng trong tất cả sự trung thực, tôi không biết đủ về tình huống của bạn để biết chắc chắn liệu cách tiếp cận của bạn có phải là một cách tốt hay không. – Abel

+0

Có lẽ tôi chỉ không hiểu generics đủ để làm theo đề nghị của bạn, xin lỗi. Trong kịch bản này, MyCollections là một cái gì đó mà bạn phải cho phép. – mbi