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);
Related
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.
In C#, I want to take an array of Type "T" where I know "T" supports the interface "IMyInterface" and:
Cast it as array of "IMyinterface"
Call a method on that array that will filter the list
Cast it back to the original type T list.
1 and 2 above work fine, but I am running into issues on step #3.
Here is my code:
IEnumerable<IMyInterface> castedArray = originalTypedArray as IEnumerable<IMyInterface>;
if (castedArray != null)
{
var filteredArray = castedArray.Where(r => r.Ids.Contains(MyId)).ToList();
IEnumerable<T> castedBackToOriginalTypeArray = filteredArray as IEnumerable<T>;
if (castedBackToOriginalTypeArray == null)
{
current = new List<T>();
}
else
{
current = castedBackArray;
}
// I need to cast back, because only my Type T has the .Id property
List<int> ids = current.Select(r => r.Id).ToList();
}
The issue is on this line:
IEnumerable<T> castedBackToOriginalTypeArray = filteredArray as IEnumerable<T>;
That always seem to return null (instead of the filtered array cast back to IEnumerable<T>.
Any suggestions here for what I might be doing wrong and how to correct cast an array of an interface back into an array of type T?
This works for me:
public class A : IA {
}
public interface IA {
}
List<A> l = new List<A> { new A(), new A(), new A() };
IEnumerable<IA> ias = l.Cast<IA>();
IEnumerable<A> aTypes = ias.Cast<A>();
Either you don't need to cast it to IEnumerable<IMyInterface>, or the runtime has correctly prevented you from writing buggy code.
Let's take a smaller example:
void SomeMethod<T>(IEnumerable<T> originalTypedArray, int MyId)
where T : class, IMyInterface
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this is important
{
if (originalTypedArray != null)
{
var filteredArray = originalTypedArray.Where(r => r.Ids.Contains(MyId));
// No need to cast to `IEnumerable<T>` here - we already have ensured covariance
// is valid in our generic type constraint
DoSomethingExpectingIEnumerableOfIMyInterface(filteredArray);
}
}
void DoSomethingExpectingIEnumerableOfIMyInterface(IEnumerable<IMyInterface> src)
{
foreach (var thing in src)
{
}
}
However, if you're not getting the collection as IEnumerable<T>, then the runtime is correctly failing the cast:
void SomeMethod<T>(IEnumerable<IMyInterface> originalTypedArray, int MyId)
We could give it a bunch of IEnumerable<Apple> assuming Apple : IMyInterface. Then you try to cast it to IEnumerable<T> where T = Banana and boom, code broken.
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;
}
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));