问题
I have a bunch of IEnumerable Collections which exact number and types is subject of frequent changes (due to automatic code generation).
It looks something like this:
public class MyCollections {
public System.Collections.Generic.IEnumerable<SomeType> SomeTypeCollection;
public System.Collections.Generic.IEnumerable<OtherType> OtherTypeCollection;
...
At runtime i want to determine each Type and it's count without having to rewrite the code after every code generation. So i am looking for a generic approach using reflection. The result i am looking for is something like:
MyType: 23
OtherType: 42
My problem is that i can't figure how to invoke the Count method properly. Here is what i have so far:
// 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);
}
}
}
回答1:
If you insist on doing it the hard way ;p
Changes:
- how you obtain countMethodInfo for a generic method
- the arguments to Invoke
Code (note obj
is my instance of 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);
}
}
}
回答2:
That is going to involve some MakeGenericMethod
- and a lot of reflection generally. Personally, I would be tempted to just simplify by ditching the generics in this case:
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;
}
You can cast to the non-generic IEnumerable
trivially, even if fetching via reflection.
回答3:
By now, the question has been answered, but I'd like to present you a trimmed down — and I think, rather trivial version — of the "calling a generic extension method", which can be used to invoke Count
reflectively:
// 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 });
The above works, because you don't need to use the generic type of IEnumerable<>
to be able to invoke Count
, which is an extension of Enumerable
and takes an argument of IEnumerable<T>
as first param (it's an extension), but you don't need to specify that.
Note that, from the reading of your question, it seems to me that you should actually use generics for your types, which adds type safety back into your project and still allows you to use Count or whatever. After all, the one thing that's certain is that all are Enumerable
, right? If so, who needs reflection?
回答4:
var count = System.Linq.Enumerable.Count(theCollection);
Edit: you say it's generated though, so can you not just generate a properties with calls to 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(); }
}
...
来源:https://stackoverflow.com/questions/3546051/how-to-invoke-system-linq-enumerable-count-on-ienumerablet-using-reflection