How to call a generic extension method dynamically? - c#

I wrote this extension method:
public static DataTable ToDataTable<T>(this IList<T> list)
{...}
It works well if called with a type known at compile time:
DataTable tbl = new List<int>().ToDataTable();
But how to call it if the generic type isn't known?
object list = new List<int>();
...
tbl = Extension.ToDataTable((List<object>)list); // won't work

This occurs because a List<int> is not a List<object> -- the List type is not covariant in its element type parameter. Unfortunately you would need to get a typed version of the generic method and call it using reflection:
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 });
An alternative may be to create a version of your extension method that accepts IList rather than IList<T>. The List<T> class implements this non-generic interface as well as the generic interface, so you will be able to call:
public static DataTable WeakToDataTable(this IList list) { ... }
((IList)list).WeakToDataTable();
(In reality you'd probably use an overload rather than a different name -- just using a different name to call out the different types.)
More info: In the reflection solution, I skipped over the problem of how to determine the list element type. This can be a bit tricky depending on how sophisticated you want to get. If you're assuming that the object will be a List<T> (for some T) then it's easy:
Type listItemType = list.GetType().GetGenericArguments()[0];
If you're only willing to assume IList<T> then it's a bit harder, because you need to locate the appropriate interface and get the generic argument from that. And you can't use GetInterface() because you're looking for a closed constructed instance of a generic interface. So you have to grovel through all the interfaces looking for one which is an instance of 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];
}
}
This will work for empty lists because it goes off the metadata, not the list content.

After having trouble to get it working with the IList<T> interface I solved it using the IList interface like itowlson proposed. It's a little bit ugly because of the _T method but it works well:
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);
}
}
}

Related

Add items to any object of ICollection<T> using reflection

I'm trying to support mapping to/from any kind of collection that implements ICollection<T> via reflection, because ICollection<T> requires implementation of the Add method.
This works fine for most common collection types, but fails for edge cases like LinkedList<T> where the Add method is hidden and can only be called by casting the LinkedList<T> to ICollection<T>.
However it's not possible to convert to ICollection<> because it is not covariant.
The other option I was considering was searching for both implicit and explicit implementations of Add, but I don't see any information on how to do this when the interface is generic?
What would be the correct approach to take?
Updated to show code snippet where I'm reflecting from xml to object mapping.
private object CollectionXmlNodeListToObject(
XmlNodeList nodeList, System.Type collectionType)
{
// this is not possible because ICollection<> is not covariant
object collection = Convert.ChangeType(
CreateInstanceOfType(collectionType), ICollection<>);
Type containedType = collectionType.GetTypeInfo().GenericTypeArguments[0];
foreach (XmlNode node in nodeList)
{
object value = CreateInstanceOfType(containedType);
if (containedType.IsClass && MetaDataCache.Contains(containedType))
value = ToObject(value, node, node.Name);
else
value = node.InnerText;
// this throws NullReferenceException when the type is LinkedList,
// because this is explicitly implemented in LinkedList
collectionType.GetMethod("Add")
.Invoke(collection, new[] { value });
}
return collection;
}
I am writing a small framework to map from object to xml using class and property attributes. So I cannot use generics because all of this is being done at runtime.
I initially was checking for IEnumerable before, but ran into other oddities with it (strings implement IEnumerable and are immutable) that I decided it was safest to stick to ICollection<>
With explicit interface implementation, the object has all the interface methods, but the object's Type does not.
So here's how to add an item to a LinkedList<T>, or any ICollection<T> through reflection:
var ll = new LinkedList<int>();
var t = typeof(int);
var colType = typeof(ICollection<>).MakeGenericType(t);
var addMethod = colType.GetMethod("Add");
addMethod.Invoke(ll, new object[] { 1 });
This functionality is met at compile-time using the Cast<T>() method. You just need a run-time version, which is pretty straightforward:
static public object LateCast(this ICollection items, Type itemType)
{
var methodDefintionForCast = typeof(System.Linq.Enumerable)
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(mi => mi.Name == "Cast")
.Select(mi => mi.GetGenericMethodDefinition())
.Single(gmd => gmd != null && gmd.GetGenericArguments().Length == 1);
var method = methodDefintionForCast.MakeGenericMethod(new Type[] { itemType });
return method.Invoke(null, new[] { items });
}
Now you can take any non-generic collection and make it generic at run-time. For example, these two are equivalent:
var list = nodeList.Cast<XmlNode>();
object list = nodeList.LateCast(typeof(XmlNode));
And you can convert a whole collection with this:
static public IEnumerable ConvertToGeneric(this ICollection source, Type collectionType)
{
return source.LateCast(collectionType.GetGenericArguments()[0]) as IEnumerable;
}
object list = nodeList.ConvertToGeneric(nodeList, typeof(ICollection<XmlNode>));
This solution works with linked lists as well as all the other collection types.
See my working example on DotNetFiddle
Pretty much all .NET collections take an IEnumerable<T> as the constructor, so you could make use of that:
private static object CollectionXmlNodeListToObject(System.Type collectionType)
{
// T
Type containedType = collectionType.GetTypeInfo().GenericTypeArguments[0];
// List<T>
Type interimListType = typeof(List<>).MakeGenericType(containedType);
// IEnumerable<T>
Type ienumerableType = typeof(IEnumerable<>).MakeGenericType(containedType);
IList interimList = Activator.CreateInstance(interimListType) as IList;
interimList.Add(null);
interimList.Add(null);
interimList.Add(null);
interimList.Add(null);
// If we can directly assign the interim list, do so
if (collectionType == interimListType || collectionType.IsAssignableFrom(interimListType))
{
return interimList;
}
// Try to get the IEnumerable<T> constructor and use that to construct the collection object
var constructor = collectionType.GetConstructor(new Type[] { ienumerableType });
if (constructor != null)
{
return constructor.Invoke(new object[] { interimList });
}
else
{
throw new NotImplementedException();
}
}
Try it online
Obviously you could optimise this by moving the list population to another method, and then maybe use your existing method as far as you can, and then use this where you can't.

C# : Method having IEnumerable<Type> as argument. What is a valid input?

I am trying to define a valid input for my method Pairwise. Pairwise takes an argument IEnumerable which I am having trouble figuring out what is exactly. I have tried alot of stuff but can never really get there.
public delegate void PairwiseDel(Type left, Type right);
public static void Pairwise(IEnumerable<Type> col, PairwiseDel del)
{
// stuff happens here which passes pairs from col to del
}
can someone plz tell and illustrate what a valid input for my method would be?
IEnumerable<T> is a very important interface in .NET library. It represents an abstraction describing a sequence of elements of type T.
This generic interface has multiple implementations:
Built-in 1-dimension arrays T[] implement IEnumerable<T>
All generic .NET collections implement IEnumerable<T>
Methods that use yield return produce IEnumerable<T>
Multiple methods in .NET LINQ library both take and return IEnumerable<T>
If you would like to test your method, pass it an array Type[]:
var items = new Type[] { typeof(int), typeof(string), typeof(long) };
Pairwise(items, (a, b) => {
Console.WriteLine("A={0}, B={1}", a.Name, b.Name);
});
This would be a valid input:
var collection = new List<Type>();
collection.Add(typeof(string));
collection.Add(typeof(int));
PairWise(collection, YourDelegateHere);

Do we have a "Contains" method in IEnumerable

I have a class in my code that is already deriving from IEnumerable.
I was wondering if there is a way that I can use a "Contains" method on its instnaces to look for a something in that list?
Do you really implement the non-generic IEnumerable, or the generic IEnumerable<T>? If you can possibly implement the generic one, your life will become a lot simpler - as then you can use LINQ to Objects, which does indeed have a Contains extension method.
Otherwise, you could potentially convert from the non-generic to generic using Cast or OfType, e.g.
bool found = nonGeneric.Cast<TargetType>().Contains(targetItem);
It would be nicer if you just implemented the generic interface to start with though :)
No, there's no such method in the IEnumerable<T> interface. There's an extension method though that you could use.
using System.Linq;
and then:
IEnumerable<string> foos = new[] { "foo", "bar", "baz" };
bool IsThereABar = foos.Contains("bar");
public static bool Contains<T>(this IEnumerable source, T value)
{
foreach (var i in source)
{
if (Equals(i, value))
return true;
}
return false;
}
If you want, you can add custom comparer as parameter ti extension method Contains

Passing a Func<T, TResult> where TResult is unknown

Note: Please re-tag and/or re-name appropriately
I have a class, FooEnumerator, that wraps a Foo and implements IEnumerable<FooEnumerator>. The Foos represent a tree-like data structure, the FooEnumerators that are enumerated are the child nodes of the current node.
Foo is a vendor supplied data object. FooEnumerator implements a bunch of custom filtering code.
class FooEnumerator : IEnumerable<FooEnumerator>
{
public Foo WrappedNode { get; private set; }
public string Name { get { return WrappedNode.Name; } }
public int Id { get{ return WrappedNode.Id; } }
public DateTime Created { get{ return WrappedNode.Created; } }
public FooEnumerator(Foo wrappedNode)
{
WrappedNode = wrappedNode;
}
public IEnumerator<FooEnumerator> GetEnumerator()
{
foreach (Foo child in this.GetChildren())
if(FilteringLogicInHere(child))
yield return new FooEnumerator(child);
}
...
}
I want to be able to sort each level of the tree with a given (arbitrary) expression, defined when the top level FooEnumerator is created, and have this expression passed down to each newly enumerated item to use.
I'd like to define the sort expression using lambda's, in the same way you would with the OrderBy function. In fact, it is my intention to pass the lambda to OrderBy.
The signiture for OrderBy is
OrderBy<TSource, TKey>(Func<TSource, TKey> keySelector)
where TKey is the return type of the given Func, but is a Type Parameter in the method signature and is figured out at compile time.
Example usage
var x = GetStartingNode();
var sort = n => n.DateTime;
var enu = new FooEnumerator(x, sort);
var sort2 = n => n.Name;
var enu2 = new FooEnumerator(x, sort2);
The sort expression would then be stored in a class variable and FooEnumerator would work like:
// pseudo-implementation
private Expression<Func<Foo, TKey>> _sortBy;
public FooEnumerator(Foo wrappedNode, Expression<Func<Foo, TKey>> sortBy)
{
WrappedNode = wrappedNode;
_sortBy = sortBy;
}
public IEnumerator<FooEnumerator> GetEnumerator()
{
foreach (Foo child in this.GetChildren().OrderBy(_sortBy))
if(FilteringLogicInHere(child))
yield return new FooEnumerator(child);
}
How can I specify the type of TKey (implicitly or explicitly) in this use case?
I don't want to hard code it as I want to be able to sort on any and all properties of the underlying Foo.
Well, you can't create a member delegate variable of type Expression<Func<Foo,TKey>> since TKey is never specified. However, you could create a member of type Expression<Func<Foo,IComparable>> which may suffice for your purposes. You could need to change your FooEnumerator constructor to accept this signature as well, of course.
EDIT: Others have suggested parameterizing your FooEnumerator so that it accepts a TKey. You can certainly do this, but you should be aware of the issues that emerge:
By parameterizing the enumerator you are then kicking the bucket down the road. Any code that wants to store a FooEnumerator<T> has to have a-priori knowledge of the type T. You could, however, implement a non-generic interface IFooEnumerator to deal with that.
Parameterizing an enumerator creates issues if you want to support ordering on multiple fields in the future. C# doesn't support generics with a variable number of type parameters, which limits the creation of generics that require multiple arbitrary types. This issue is harder to deal with, since it's awkward to start creating FooEnumerator<T>, FooEnumerator<T1,T2>, FooEnumerator<T1,T2,T3...>, and so on.
You can also parameterize your Enumerator:
class FooEnumerator<TKey> {
// ... All your 'pseudo' code would work here
}
I recommend programming against the interface using IComparable however.

Generate a generic list of some given type

My friend is trying to create a utility function that is given some Type and in that function it creates a generic List of that type. We're having trouble creating that list:
public static List<T> GetQueryResult(string xpathQuery, Type itemType) {
// this line does not work:
List<itemType> lst = new List<itemType>();
return lst;
}
Are there any easy solutions to this?
UPDATE:
Is there any way to basically do this???
List<T> lst = new List<T>();
foreach (Sitecore.Data.Items.Item i in items) {
lst.Add(new T(i));
}
public static List<T> GetQueryResult<T>(string xpathQuery/*, Type itemType you don't need this because you specified the Type with T*/) {
// this line works now:
List<T> lst = new List<T>();
return lst;
}
Then you would call the method like so:
List<int> results = GetQueryResult<int>("xpathQuery");
Edit:
Are you wanting to do something like this?
List<YourType> lst = items.Select<Sitecore.Data.Items.Item, YourType>(
siteCoreItem => new YourType()
{
PropertyA = siteCoreItem.PropertyA,
}
);
If YourType inherrits from Sitecore.Data.Items.Item you can use Cast:
List<YourType> list = items.Cast<YourType>();
Define that method like this:
public static List<T> GetQueryResult<T>(string xpathQuery)
{
List<T> lst = new List<T>();
// do stuff
return lst;
}
and call it like this:
List<SomeType> items = SomeClass.GetQueryResult<SomeType>("query");
It is possible using reflection, for example:
var type = typeof(int); // var type = itemType : put this line to fit the method
var genericListType = typeof(List<>).MakeGenericType(type);
var genericList = Activator.CreateInstance(genericListType);
Assert.IsTrue(genericList is List<int>);
In your example, ehere do you get T from that you use in the return type? Maybe there is no need to use here reflection.
If you do not get T as generic argument then you cannot return the List as generic List and the method will have to return a non generic type (like IList instead of List).
While Elisha's answer shows you how you can create a constructed generic type from a Type instance, it's not going to help you because what I think you want to do is not possible: the signature of the GetQueryResult method is illegal because T is unspecified (unless the method is a member of a generic type itself).
The method will not compile as given.
If you already know the type, you can change it to
public static List<T> GetQueryResult<T>(string xpathQuery)
{
var lst = new List<T>();
return lst;
}
but that's probably not what you want...
Generic type arguments are resolved compile time, so to have the code working you'd need to pass itemType as a type argument or change the return type to IList And Them use the solution given by ELisha but that would mean loosing type information on the Call site
Answer to the updated question:
public List<T> GetQueryResult<T>(string xPathQuery)
{
var items = ;// logic to get items
var list = new List<T>();
foreach (Sitecore.Data.Items.Item item in items)
{
list.Add((T) Activator.CreateInstance(typeof(T), item));
}
return list;
}
I assume that T has a constructor that gets Sitecore.Data.Items.Item, if it won't have the code will fail at runtime.
There must be a safer way to do it, it'll be better if you can give wider context to the problem.
As others have demonstrated, the only way to solve your updated question for any T is with reflection. However, if T is restricted to a well known set of types that you can modify, you could do this:
public interface IItemContainer
{
void SetItem(Sitecore.Data.Items.Item item);
}
public static List<T> GetQueryResult<T>(string xpathQuery)
where T : IItemContainer, new() {
IList<Sitecore.Data.Items.Item> items = GetAListOfItemsSomehow(xpathQuery);
List<T> result = new List<T>();
foreach (Sitecore.Data.Items.Item item in items) {
T obj = new T();
obj.SetItem(item);
result.add(obj);
}
return result;
}
Any types you want to use for T would then have to implement IItemContainer.
public static List<T> GetQueryResult<T>(string xpathQuery) {
List<T> lst = new List<T>();
return lst;
}
is the only way if you want static typing. Otherwise you could do
public static IList GetQueryResults(string xpathQuery, Type itemType) {
Type tp = typeof(List<>).MakeGenericType(itemType);
IList lst = (IList)Activator.CreateInstance(tp);
return lst;
}
but using a non-generic list would probably be better in that case.
Edit: You asked another question in the same post:
The 3 ways of creating an instance of a generic type are
use the where T : new() constraint and use the default constructor (doesn't seem good enough for you).
Use reflection. Rarely the best idea.
Specify a creator function
like this:
public static List<T> GetQueryResults<T>(string xpathQuery, Func<int, T> creator) {
var result = new List<T>();
foreach (i in something)
result.add(creator(i));
return result;
}
and then invoke it like:
List<int> l = GetQueryResults("something", i => new MyObject(i));

Categories