How to invoke System.Linq.Enumerable.Count<> on IEnumerable<T> using Reflection? - c#

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);
}
}
}

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);
}
}
}

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.

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?

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(); }
}
...

Related

In a generic method, how to pass in classes based on conditions and capture return value

I have a generic method that accepts T like this and returns IEnumerable. Based on some condition, I have to pass in a different class. Then I iterate the results.
public IEnumerable<T> DoWorkGetResult<T>(string param)
yield return (T)typeof(T).GetMethod("CreateRow").Invoke(null, new object[] { someOtherParam });
//etc...
object resultRows;
if (param == "some condition")
resultRows = _data.DoWorkGetResult<TClass1>(param);
else
resultRows = _data.DoWorkGetResult<TClass2>(param);
foreach (var dataRow in resultRows) //error, object hasno public def for GetEnumerator
{
}
The problem is this: I don't know how to initialize the return value. I used object, which is fine. But how do I then loop it?
as Ron Beyer mentioned, resultRows can't be iterated through if it's just an object. change it from object resultRows to IEnumerable<T> resultRows and the foreach should compile.
The return type could be any generic collection mostly a interface IEnumerable, IList, etc. Not necessary a IEnumerable<T> just a simple IEnumerable or IList, on the other side you will know what class are you working on and cast when the method returns the result.
I use IList in this cases give you more functionality.
var typeClass = typeof(T); // Or maybe Type.GetType("myNamespace.myClass")
var listInstance = (System.Collections.IList)typeof(List<>)
.MakeGenericType(typeClass)
.GetConstructor(Type.EmptyTypes)
.Invoke(null);
// Add Some instance from T
var objectInstance = Activator.CreateInstance(typeClass); // Create a Instance
listInstance.Add(objectInstance);
Just another idea. Have fun :)

How do you call a generic method with out parameters by reflection?

Suppose I have a class like this, containing a generic method with an out parameter:
public class C
{
public static void M<T>(IEnumerable<T> sequence, out T result)
{
Console.WriteLine("Test");
result = default(T);
}
}
From reading the answers to a couple of other questions (How to use reflection to call generic Method? and Reflection on a static overloaded method using an out parameter), I thought I might be able to invoke the method via reflection as follows:
// get the method
var types = new[] { typeof(IEnumerable<int>), typeof(int).MakeByRefType() };
MethodInfo mi = typeof(C).GetMethod(
"M", BindingFlags.Static, Type.DefaultBinder, types, null);
// convert it to a generic method
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) });
// call it
var parameters = new object[] { new[] { 1 }, null };
generic.Invoke(null, parameters);
But mi is coming back null. I've tried using object instead of int in the types array but that doesn't work either.
How can I specify the types (needed for the out parameter) for a generic method before the call to MakeGenericMethod?
This will let you call the method:
MethodInfo mi = typeof(C).GetMethod("M");
MethodInfo generic = mi.MakeGenericMethod(new[] { typeof(int) });
var parameters = new object[] { new[]{1},null};
generic.Invoke(null, parameters);
And to get the out parameter:
Console.WriteLine((int)parameters[1]); //will get you 0(default(int)).
I'm still interested to know what the syntax is for specifying an array of template types, or if it's not possible
I don't think it's possible to pass that kind of detailed type specification to GetMethod[s]. I think if you have a number of such Ms to look through, you have to get them all and then filter by the various properties of the MethodInfos and contained objects, eg as much of this as is necessary in your particular case:
var myMethodM =
// Get all the M methods
from mi in typeof(C).GetMethods()
where mi.Name == "M"
// that are generic with one type parameter
where mi.IsGenericMethod
where mi.GetGenericArguments().Length == 1
let methodTypeParameter = mi.GetGenericArguments()[0]
// that have two formal parameters
let ps = mi.GetParameters()
where ps.Length == 2
// the first of which is IEnumerable<the method type parameter>
where ps[0].ParameterType.IsGenericType
where ps[0].ParameterType.GetGenericTypeDefinition() == typeof(IEnumerable<>)
where ps[0].ParameterType.GetGenericArguments()[0] == methodTypeParameter
// the second of which is ref <the method type parameter>
where ps[1].ParameterType.IsByRef
where ps[1].ParameterType.GetElementType() == methodTypeParameter
select mi;
You've passed parameters that will find M<T>(IEnumerable<int>, ref int).
You need to find M(IEnumerable<T>, ref T) (the distinction between ref and out exists only in the C# language; reflection only has ref).
I'm not sure how to pass that; you may need to loop through all methods to find it.
On an unrelated note, you need to pass more BindingFlags:
BindingFlags.Public | BindingFlags.Static
This is a well known-problem; to find the method, you need to know its type parameter, but you can't know its type parameter without knowing the method first...
An obvious but inelegant solution is to loop through all methods until you find the right one.
Another option is to take advantage of the Linq Expression API:
public static MethodInfo GetMethod(Expression<Action> expr)
{
var methodCall = expr.Body as MethodCallExpression;
if (methodCall == null)
throw new ArgumentException("Expression body must be a method call expression");
return methodCall.Method;
}
...
int dummy;
MethodInfo mi = GetMethod(() => C.M<int>(null, out dummy));

Casting Results from Generic Method Invocation?

I'm currently messing about with generics and I'm trying to write a function that I can call to load everything from a database table simply by specifying the table name.
I'm most of the way there; my generic methods all seem to work, but I'm not quite sure how to cast my results into something usable.
This is the guts of the method so far:
private static List<EntityCodeBase> GetCodeLoadResults(CodeTables table)
{
List<EntityCodeBase> results = new List<EntityCodeBase>();
Assembly assm = Assembly.Load(new System.Reflection.AssemblyName("RR"));
Type tableType = assm.GetTypes().Where(u => u.Name.ToLower() == table.ToString().ToLower()).FirstOrDefault();
MethodInfo mi = typeof(SpecificEntity).GetMethod("LoadAll");
mi = mi.MakeGenericMethod(tableType);
mi.Invoke(null, null); //how can I cast the resulting object into a List<EntityCodeBase> ?
return results;
}
Assuming SpecificEntity.LoadAll returns a list of some type derived from EntityCodeBase, you can't cast directly to a List<EntityCodeBase> but you can cast to IEnumerable<EntityCodeBase>. Then you can create a new list:
var ecbList = (IEnumerable<EntityCodeBase>)mi.Invoke(null, null);
return list.ToList();
It might be cleaner however, if you can get the table name from the entity type, either directly by name, using attributes, or using a map. Then you can make GetCodeLoadResults generic in the result type e.g.
private static List<T> GetCodeLoadResults() where T : EntityCodeBase
{
Assembly assm = Assembly.Load(new System.Reflection.AssemblyName("RR"));
Type tableType = //get table type from T
MethodInfo mi = typeof(SpecificEntity).GetMethod("LoadAll");
mi = mi.MakeGenericMethod(tableType);
return (List<T>)mi.Invoke(null, null);
}
If you're not using .Net 4, you can't cast a List<TDerived> to an IEnumerable<TBase>, so you'll have to cast to IEnumerable first:
return ((System.Collections.IEnumerable)mi.Invoke(null, null))
.Cast<EntityCodeBase>()
.ToList();

How to return a specific type of object from List<object>

I have a List<object> which is a collection of various type of objects.
I am writing a helper method which will return a specific type of object. The helper method will accept type name as string parameter.
Note: I am using 3.5 framework.
If you need to use a string as parameter you can't rely on OfType<T>() extension method. Fortunately it's easy to emulate:
public IEnumerable<object> OfType(this List<object> list, string typeName)
{
return list.Where(x => x != null && x.GetType().Name == typeName);
}
As pointed out by #ChrisSinclair in the comment this solution does not manage conversions, casts and inheritance/interfaces. Casts (because of user defined conversion operators) and conversions (because of TypeConverters and the IConvertible interface) are little bit more tricky. For simple (implicit) casts (like with inheritance and interfaces) you can use this:
public IEnumerable<object> OfType(this List<object> list, string typeName)
{
Type type = Type.GetType(typeName);
return list.Where(x => x != null && type.IsAssignableFrom(x.GetType()));
}
How to perform conversions (even with CUSTOM CONVERSION OPERATORS) at run-time
I found I needed something like the code I posted in this answer but I had to extend it a little bit, here a better implementation that takes care of custom casts and conversions.
Put everything inside a CastExtensions class (or update code if you don't) then declare this small enum for its options:
[Flags]
public enum CastOptions
{
None = 0,
ExcludeNulls = 1,
UseConversions = 2
}
The problem is that C# in general is a statically typed language, it means that almost everything (about types) must be known at compile time (then to perform a cast you have to know type your want to cast to at compile time). This function handles simple cases (like derivation) and more complex ones (interfaces, custom conversion operators - casts - and conversions - when required).
public static IEnumerable<object> OfType(this List<object> list,
string typeName, CastOptions options)
{
Type type = Type.GetType(typeName);
foreach (var obj in list)
{
if (Object.ReferenceEquals(obj, null))
{
if (options.HasFlag(CastOptions.ExcludeNulls))
continue;
yield return obj;
}
var objectType = obj.GetType();
// Derived type?
if (type.IsAssignableFrom(objectType))
yield return obj;
// Should we try to convert?
if (!options.HasFlag(CastOptions.UseConversions))
continue;
// Castable?
object convertedValue = null;
try
{
var method = typeof(CastExtensions)
.GetMethod("Cast", BindingFlags.Static|BindingFlags.NonPublic)
.MakeGenericMethod(type);
convertedValue = method.Invoke(null, new object[] { obj });
}
catch (InvalidCastException)
{
// No implicit/explicit conversion operators
}
if (convertedValue != null)
yield return convertedValue;
// Convertible?
if (options.HasFlag(CastOptions.UseConversions))
{
try
{
IConvertible convertible = obj as IConvertible;
if (convertible != null)
convertible.ToType(type, CultureInfo.CurrentCulture);
}
catch (Exception)
{
// Exact exception depends on the source object type
}
}
}
}
Note that conversion may be or not equivalent to a cast, actually it depends on
the implementation and the exact types involved in the operation (that's why you
can enable or disable this feature through options).
This is a small helper function needed for cast at run-time:
private static T Cast<T>(object obj)
{
return (T)obj;
}
We may emit this code at run-time (I suppose even using expressions but I didn't try) but a small helper method will generate exactly the code we need (conversion from an object to a generic known at run-time type). Note that this cast function doesn't work as expected for value types, for example:
int a = 1;
float a = Cast<float>(a); // Run-time error
This is because (object)1 cannot be converted to anything else than int (this is true for all boxed value types). If you're using C# 4.0 you should change object for parameter obj to dynamic and everything will work as expected (for all types).
Maybe something like that :
var ofTypeTypeA = myList.OfType<TypeA>();
A clean way is to force the user to specify the type as type to avoid loose strings in your application.
Then you could use generics and just use the type you are interested in. That would also allow the caller to skip the cast when using the IEnumerable later.
So instead of this:
List<object> newList = GetOfType(myList, "SomeObject");
// CAST!!
SomeObject someObject = newList[0] as SomeObject;
if (someObject != null)
// use object
you would just do:
IEnumerable<SomeObject> newList = myList.OfType<SomeObject>();
foreach (SomeObject someObject in newList){
// no cast neccessary
This makes it unsensitive in the future if you should rename the class SomeObject (because refactoring tools would pick up on the class name instead of the string)
You can use Enumerable.OfType
var input = new List<object>();
input.Add(1);
input.Add("foo");
var bar = input.OfType<string>();
I guess you need to cast a single object extracted from the list to a strongly-typed object. And not to cast all the list to it. Otherwise use List<MyType>.
So I would go with this: How to cast to a type in C#.
You could use the is operator (or pass the type and check for that also using is). Here is an example of using the is operator:
foreach (var ctl in ControlsList)
{
if (ctl is CheckBox)
//Do this
else if (ctl is TextBox)
//DoThis
}
And by passing the type as string in the parameter, you could do something similar to get the type to test against:
Type t = System.Type.GetType("System.Int32");

How can I find the type of T in a c# Generic collection of T when all I know is the type of the collection?

I have an array of PropertyInfo representing the properties in a class. Some of these properties are of type ICollection<T>, but T varies across the properties - I have some ICollection<string>, some ICollection<int>, etc.
I can easily identify which of the properties are of type ICollection<> by using the GetGenericTypeDefinition() method on type, but I am finding it impossible to get the type of T - the int or string in my example above.
Is there a way to do this?
IDocument item
PropertyInfo[] documentProperties = item.GetType().GetProperties();
PropertyInfo property = documentProperties.First();
Type typeOfProperty = property.PropertyType;
if (typeOfProperty.IsGenericType)
{
Type typeOfProperty = property.PropertyType.GetGenericTypeDefinition();
if (typeOfProperty == typeof(ICollection<>)
{
// find out the type of T of the ICollection<T>
// and act accordingly
}
}
If you know it'll be ICollection<X> but don't know X, that's fairly easy with GetGenericArguments:
if (typeOfProperty.IsGenericype)
{
Type genericDefinition = typeOfProperty.GetGenericTypeDefinition();
if (genericDefinition == typeof(ICollection<>)
{
// Note that we're calling GetGenericArguments on typeOfProperty,
// not genericDefinition.
Type typeArgument = typeOfProperty.GetGenericArguments()[0];
// typeArgument is now the type you want...
}
}
It gets harder when the type is some type which implements ICollection<T> but may itself be generic. It sounds like you're in a better position :)
I believe this is what you are looking for:
typeOfProperty.GetGenericArguments()[0];
That will return you the T part of a generic List<T> for example.
Jon's solution will yield T. Depending on the context, you might need to access the getter return type instead in order to get int, string, etc. For example...
// The following example will output "T"
typeOfProperty = property.PropertyType.GetGenericTypeDefinition();
Type genericDefinition = typeOfProperty.GetGenericTypeDefinition();
if (genericDefinition == typeof(ICollection<>))
{
Type t1 = typeOfProperty.GetGenericArguments()[0];
Console.WriteLine(t1.ToString());
}
// Instead you might need to do something like the following...
// This example will output the declared type (e.g., "Int32", "String", etc...)
typeOfProperty = property.GetGetMethod().ReturnType;
if (typeOfProperty.IsGenericType)
{
Type t2 = typeOfProperty.GetGenericArguments()[0];
Console.WriteLine(t2.ToString());
}

Categories