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.
Related
I am handling classes that wrap collections. For example:
public class CollA
{
public List<SomeType> Items {get;set;}
// other properties I'm not interested in
}
I am guaranteed that the collection classes will have exactly ONE property that is of List<T>
Now, I find myself with a requirement such that I may have many instances of CollA and I am asked to return a new instance of CollA where the property Items contains a union of the Items properties of the individual CollA instances. So, for example:
var A = new CollA(Items = new List<SomeType>
{
new SomeType("A"), new SomeType("B")
};
var B = new CollA(Items = new List<SomeType>
{
new SomeType("C"), new SomeType("D")
};
var result = SomeMythicalCombine(A, B);
// result.Items == { new SomeType("A"), new SomeType("B"), new SomeType("C"), new SomeType("D") }
This, if the types are all known at compile time is easy, but I need to do it with the types not being known until runtime.
I've got part of the way, I think, using reflection....
public T SomeMythicalCombine (params object[] collections)
{
var collectionType = typeof(T);
var listProperty = collectionType.GetProperties()
.Single(p=> typeof(IList).IsAssignableFrom(p.PropertyType));
var listPropertyName = listProperty.Name;
var result = Activator.CreateInstance(collectionType);
var innerType = listProperty.PropertyType.GenericTypeArguments[0];
var listType = typeof(List<>).MakeGenericType(innerType);
var list = Activator.CreateInstance(listType);
foreach(var collection in collections)
{
var listValues = collection.GetType().GetProperty(listPropertyName).GetValue(collection);
// listItems is an object here and I need to find a way of casting it
// to something I can iterate over so I can call (list as IList).Add(something)
}
// Then, I think, all I need to do is set the appropriate property on the
// the result item
result.GetType().GetProperty(listPropertyName).SetValue(result, list);
return result as T;
}
Can anyone fill in the gap in my thinking, please?
So basically if you know the type at compile time, you can do this:
var result = new CollA { Items = new[] { A, B }.SelectMany(c => c.Items).ToList() };
If you can require all your collection wrappers to implement an interface, it should be pretty simple to extract this into a generic method.
public interface ICollectionWrapper<T> { List<T> Items { get; set; } }
T SomeMythicalCombine<T, T2>(params T[] wrappers) where T : ICollectionWrapper<T2>, new()
{
return new T() { Items = wrappers.SelectMany(w => w.Items).ToList() };
}
That presupposes you can call the method with the right generic parameter. If your calling code knows the types of the collections you're dealing with, you can do this:
var result = SomeMythicalCombine(A, B);
But honestly if your calling code knows that, you might be better off using the first code snippet: it's concise and clear enough. Assuming you literally have a collection of objects that you just happen to know will all have the same run-time type, you should be able to use a little reflection to get that type and invoke the helper method with the right generic parameters. It's not ideal, but it might be faster/simpler than writing the entire method to work using reflection.
you can do this : var combined = A.Items.Concat(B.Items).
However, if the property is a part of interface or base class implementation, then you can target the implementation instead something like this :
public IList<TResult> SomeMythicalCombine<TResult>(params IInterface[] collection) // use interface or base class
{
// assuming that all collection would have the same element type.
}
if it is not a part of other implementations, then you can implement an interface and apply it to all classes, this would be an insurance that this collection will always be there as long as the class implements the interface.
if it's hard to achieve that, then you can and you see that reflection is your best option, you can use something like this :
// assuming all collections have the same property of type List<TResult> type.
// if they're different, then return an object instead. and change List<TResult> to IList
public IEnumerable<TResult> CombineLists<T, TResult>(params T[] instances)
where T : class
{
if (instances?.Any() == false) yield break;
foreach(var obj in instances)
{
if (obj == null) continue;
var list = obj.GetType()
.GetProperties()
.FirstOrDefault(p => typeof(List<TResult>).IsAssignableFrom(p.PropertyType))
?.GetValue(obj) as List<TResult>;
if (list?.Count == 0) continue;
foreach (var item in list)
yield return item;
}
}
usage :
var combined = CombineLists<CollA, string>(A, B);
How can I get the key values of a generic IDictionary<,> using reflection.
This is the type of thing I want to do.
public static string Format<T>(T item)
{
if (item.GetType().GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
{
// I know it's a IDictionary so figure out what the types are
Type keyType = item.GetType().GetGenericArguments()[0];
Type valueType = item.GetType().GetGenericArguments()[1];
//Now cast it to the correct IDictionary. How do I properly inject the type here?
var keyList = ((IDictionary<keyType, valueType>)item).Select(x => x.Key.ToString()).ToArray<string>();
}
}
Edit: Clarified that I want to use reflection
I think you are overcomplicating things. When I look at your code, you are trying to do the following:
myDictionary.Keys.Select(k => k.ToString()).ToArray();
So,
public string Format<T>(T value) {
if (##item is dictionary) {
var items = name.GetType().GetProperty("Keys", BindingFlags.Instance | BindingFlags.Public).GetValue(item) as IEnumerable;
if (items == null) throw new ArgumentException("Dictionary with no keys?");
string[] data = items.OfType<object>().Select(o => o.ToString()).ToArray();
}
}
Unfortunately you cannot pass a System.Type as a type argument. If you want to use the IDictionary<,> properties, you need to do a little more reflection.
var dictionaryType = typeof(IDictionary<,>).MakeGenericType(keyType, valueType);
var keysProperty = dictionaryType.GetProperty("Keys");
var keys = ((IEnumerable)keysProperty.GetValue(item)).OfType<object>().Select(k => k.ToString()).ToArray<string>();
However, if all you want are the keys, you can just use the non-generic IDictionary interface, and don't bother with reflection.
var dictionary = item as IDictionary;
if (dictionary != null)
{
var keyList = dictionary.Select(x => x.Key.ToString()).ToArray<string>();
}
You got a few problems here. One minor issue is you don't use the found interface for getting the generic arguments.
The other bigger problem is you're trying to use dynamically found types in types defined statically at compile time (IDictionary<keyType, valueType>).
If you want to continue down this road it's using reflection, which can be hard. Depending on your goal you could choose another path and try this out:
public static string Format<TKey,TValue>(IDictionary<TKey,TValue> item)
{
var keyList = item.Select(x => x.Key.ToString()).ToArray();
// do some work with keyList and return a string.
}
This will still work public static string Format<T>(T item) handling other types, so implementing this beside it:
public static string Format<T>(T item)
{
// handle non IDictionary<,> objects here
}
Making you able to call format anywhere:
Format(new Dictionary<string,int> { { "hello world", 1337 } });
Format("string");
Format(new { Hello = "World" });
The issue is that you're trying to get generic code parameters from Type objects. This is not directly possible, because Type is a class like nay other, it just represents a class, and type arguments are used to compile the method. Thus, you can't get new type arguments within the underlying code...
That being said, You could add extra generic parameters, with specific constraints,
public static Format<T, TKey, TValue>(T item)
where T : IDictionary<TKey, TValue>
Or, to be more succinct
public static Format<TKey, TValue>(IDicitonary<TKey, TValue> item)
This can then be called using dynamic so as to ensure the proper arguments are used
public static string Format<T>(T item)
{
if (item.GetType().GetInterfaces().Any(x => x.IsGenericType && x.GetGenericTypeDefinition() == typeof(IDictionary<,>)))
{ FormatDictionary((dynamic) item); }
}
private static FormatDictionary<TKey, TValue>(IDicitonary<TKey, TValue> item)
If you must perform these calls with Type objects, you'll have do so via metacode - either with more reflection calls, or by compiling a method using Expressions
I 'm trying to build a DI container and I 've stumbled on to the following problem: I have a method that retrieves a list of registered instances for a given type and I want to use that to inject IEnumerable<T> properties in a given object. An example of what I am trying to achieve would be the following:
class A { public IList<IExample> Objects { get; set; } }
class B: IExample {}
class C: IExample {}
Container.Register<IExample>(new B());
Container.Register<IExample>(new C());
var obj = new A();
Container.Inject(A);
Debug.Assert(A.Objects != null && A.Objects.Count == 2);
My Retrieve method returns an IList<object>, mainly because I have no type information at that moment, so I am attempting to convert that list into a List<T> at injection time. Here is a succint form of the methods doing the work:
public virtual IList<object> Retrieve(Type type)
{
var instances = Registry[type];
foreach(var instance in instances)
Inject(type, instance); // omitted
return instances;
}
public virtual void Inject<T>(T instance)
{
var properties = typeof (T).GetProperties();
foreach (var propertyInfo in properties)
{
var propertyType = propertyInfo.PropertyType;
if (!IsIEnumerable(propertyType)) continue;
var genericType = propertyType.GetGenericArguments()[0];
propertyInfo.SetValue(instance,
GetListType(genericType, Retrieve(genericType)), null);
}
}
protected virtual object GetListType(Type type, IEnumerable<object> items)
{
return items.Select(item => Convert.ChangeType(item, type)).ToList();
}
The code returns the error: System.InvalidCastException : Object must implement IConvertible. Sadly, I don't know how to proceed from here. Perhaps I am doing this all wrong. I 've thought of using generics or injecting multiple properties by hand, but I'd really like to not have to do that.
Thanks in advance for any help or ideas.
You could create a generic list like this:
public virtual IList Retrieve(Type type)
{
// ...
listType = typeof(List<>).MakeGenericType(new Type[] { type });
IList list = (IList)Activator.CreateInstance(listType);
// ...
return list
}
this list can be casted to IList<T>, because it is one.
You could consider to use IEnumerable and Cast<T>, but then you don't have an instance of a list. I don'^t know how important it is to have one.
i'm trying to make a mixed collection of Types. I know the types at the start.. but I can't seem to figure out the syntax to make the collection, etc.
eg.
....
// I leave the typo there, for embarrassment :(
Initialize(new []{ typeof(Cat), typeof(Dog), typeof(JohnSkeet) });
...
public Foo Initialize(IEnumerable<Type> types)
{
// for each type, set up the inmemory storage.
foreach(var type in types)
{
// ????
// Create an empty list, which will only contain this 'type'
// I'm guessing, an IDictionary<type, ICollection<type>>().. thingy ?
}
}
public ICollection<Type> SomeTypeData(Type type)
{
// Return the collection, for this type.
}
Does this mane sense? Is this possible?
Okay, now that I think I know what you want, it would look something like this:
// This can't really be *properly* statically typed
private readonly Dictionary<Type, object> typeMap = new
Dictionary<Type, object>();
public Foo Initialize(IEnumerable<Type> types)
{
Type genericListType = typeof(List<>);
foreach(var type in types)
{
// MakeGenericType is really badly named
Type constructedListType = genericListType.MakeGenericType(type);
typeMap[type] = Activator.CreateInstance(constructedListType);
}
}
// We can't express this particularly safely either,
// although we *could* return the non-generic IList
public object SomeTypeData(Type type)
{
return typeMap[type];
}
// This *is* statically typed, although we need to cast inside
public IList<T> SomeTypeData<T>()
{
return (IList<T>) typeMap[typeof(T)];
}
See this blog post for a similar example.
Note that basically you're trying to represent something which generics simply can't handle, in terms of the internal dictionary type... and the first form of SomeTypeData can't be statically typed either... because that means knowing the type at compile time when we'll only actually be given it at execution time.
It looks to me like you're trying to create some kind of instance repository; a class that stores a list of instances of a given type.
Here's an example implementation. I've included both a generic and non-generic version of the SomeTypeData method:
public class InstanceRepository
{
private IDictionary<Type, ICollection> _Instances = new Dictionary<Type, ICollection>();
public ICollection SomeTypeData(Type type)
{
ICollection instanceList;
if (!_Instances.TryGetValue(type, out instanceList))
{
// this type does not exist in our dictionary, so let's create a new empty list
// we could do this:
//instanceList = new List<object>();
// but let's use reflection to make a more type-specific List<T> instance:
instanceList = (ICollection)Activator.CreateInstance(typeof(List<>).MakeGenericType(type));
// now add it to the dictionary
_Instances.Add(type, instanceList);
}
// Return the collection, for this type.
return instanceList;
}
public IList<T> SomeTypeData<T>()
{
Type type = typeof(T);
ICollection instanceList;
if (!_Instances.TryGetValue(typeof(T), out instanceList))
{
instanceList = new List<T>();
_Instances.Add(type, instanceList);
}
// here we are assuming that all of the lists in our dictionary implement IList<T>.
// This is a pretty safe assumption, since the dictionary is private and we know that
// this class always creates List<T> objects to put into the dictionary.
return (IList<T>)instanceList;
}
}
Below is a usage example:
Generic:
InstanceRepository repository = new InstanceRepository();
var listOfCats = repository.SomeTypeData<Cat>();
listOfCats.Add(new Cat());
Cat firstCat = listOfCats[0];
Console.WriteLine(listOfCats.GetType().FullName);
Non-Generic:
InstanceRepository repository = new InstanceRepository();
var listOfCats = (IList<Cat>)repository.SomeTypeData(typeof(Cat));
listOfCats.Add(new Cat());
Cat firstCat = listOfCats[0];
Console.WriteLine(listOfCats.GetType().FullName);
I guess you want something like
_dict[typeof(Cat)]=new List<Cat>();
_dict[typeof(Dog)]=new List<Dog>();
only programatically based on given types?
Something like this should work:
public void Initialize(IEnumerable<Type> types)
{
foreach(var type in types)
{
var list = Activator.CreateInstance(Type.GetType("System.Collections.Generic.List`1").MakeGenericType(type));
_cache[type] = list;
}
}
public ICollection<T> Get<T>()
{
object list;
if (_cache.TryGetValue(typeof(T), out list)
{
return list as ICollection<T>;
}
else
{
...
}
}
var cats = Get<Cat>();
I'm not sure I fully understand you're question, but if you already have an IEnumerable<Type> which contains an enumeration of Type objects, then why not just use that to initialize some type of Collection (such as List<Type>)?
public ICollection<Type> Initialize(IEnumerable<Type> types)
{
ICollection<Type> collection = new List<Type>(types);
return collection;
}
Is there any way to determine if an object is a generic list? I'm not going to know the type of the list, I just know it's a list. How can I determine that?
This will return "True"
List<int> myList = new List<int>();
Console.Write(myList.GetType().IsGenericType && myList is IEnumerable);
Do you care to know if it's exactly a "List"... or are you ok with it being IEnumerable, and Generic?
The following method will return the item type of a generic collection type.
If the type does not implement ICollection<> then null is returned.
static Type GetGenericCollectionItemType(Type type)
{
if (type.IsGenericType)
{
var args = type.GetGenericArguments();
if (args.Length == 1 &&
typeof(ICollection<>).MakeGenericType(args).IsAssignableFrom(type))
{
return args[0];
}
}
return null;
}
Edit: The above solution assumes that the specified type has a generic parameter of its own. This will not work for types that implement ICollection<> with a hard coded generic parameter, for example:
class PersonCollection : List<Person> {}
Here is a new implementation that will handle this case.
static Type GetGenericCollectionItemType(Type type)
{
return type.GetInterfaces()
.Where(face => face.IsGenericType &&
face.GetGenericTypeDefinition() == typeof(ICollection<>))
.Select(face => face.GetGenericArguments()[0])
.FirstOrDefault();
}
The accepted answer doesn't guarantee the type of IList<>.
Check this version, it works for me:
private static bool IsList(object value)
{
var type = value.GetType();
var targetType = typeof (IList<>);
return type.GetInterfaces().Any(i => i.IsGenericType
&& i.GetGenericTypeDefinition() == targetType);
}
Try:
if(yourList.GetType().IsGenericType)
{
var genericTypeParams = yourList.GetType().GetGenericArguments;
//do something interesting with the types..
}
The question is ambiguous.
The answer depends on what you mean by a generic list.
A List<SomeType> ?
A class that derives from List<SomeType> ?
A class that implements IList<SomeType> (in which case an array can be considered to be a generic list - e.g. int[] implements IList<int>)?
A class that is generic and implements IEnumerable (this is the test proposed in the accepted answer)? But this will also consider the following rather pathological class to be a generic list:
.
public class MyClass<T> : IEnumerable
{
IEnumerator IEnumerable.GetEnumerator()
{
return null;
}
}
The best solution (e.g. whether to use GetType, IsAssignableFrom, etc) will depend on what you mean.
Theres a GetType() function in the System.Object class. Have you tried that?