C# Cast Object in generic list - c#

I have an object with properties. Some properties are generic list. For example IList<IArticle> or IList<IProduct> and so on.
I iterate over all properties in the object with myObject.GetType().GetProperties() and search for properties which are from type IList.
I can identify the IList-properties and would like to iterate over the list. But there is my problem. I can't cast the listProperty (which is of type object) into generic list. The problem is that the properties are different generic types. To cast into IList will work only for the property which are from type IList<IArticle>, but not for the rest like IList<IProdukt>...
Cast into IList<object> is always null.
Here is a example code:
foreach (var myProperty in myObject.GetType().GetProperties())
{
//get generic property (type IList)
if (myProperty.PropertyType.IsGenericType)
{
PropertyInfo propInfo = myObject.GetType().GetProperty(myProperty.Name);
Type propType = myObject.GetType().GetProperty(myProperty.Name).PropertyType;
var listProperty = propInfo.GetValue(myProperty);
foreach (var test in (listProperty as IList<???>))
{
//Do some magic
}
}
}

Here is the solution to iterate over the list. Just cast to IEnumerable.
Don't forget to include using System.Collections;.
foreach (var myProperty in myObject.GetType().GetProperties())
{
//get generic property (type IList)
if (myProperty.PropertyType.IsGenericType)
{
var listProperty = myProperty.GetValue(myObject) as IEnumerable;
foreach (var test in listProperty)
{
//Do some magic
}
}
}

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.

Why IntelliSense doesn't work as expected in a foreach loop?

While typing the following code snippet, I noticed that Intellisense didn't work as expected:
StringBuilder sb = new StringBuilder();
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(sb))
{
var name = prop.DisplayName;
Console.WriteLine("{0}", name);
}
In the foreach statement, if I start typing prop.Di, Intellisense completes as expexcted with prop.DisplayName. However, I use the var keyword instead of PropertyDescriptor, then I see only the method inherited from object.
As TypeDescriptor.GetProperties() returns a collection of TypeDescriptor, I thought that Visual Studio would be able to infer the correct type for prop.
Why is it not working?
GetProperties returns a PropertyDescriptorCollection which only implements IEnumerable, not IEnumerable<PropertyDescriptor>. If you use var, the type of prop is inferred to be object not PropertyDescriptor.
It returns PropertyDescriptorCollection, and the return type of GetEnumerator method of PropertyDescriptorCollection is IEnumerator (non-generic). So even you can write
foreach (int prop in TypeDescriptor.GetProperties(sb))
And you won't get any exception in compile time,but at runtime you will get an InvalidCastException.
Here is an example that demonstrates the difference:
Normally, this is invalid:
foreach(string x in new List<int>())
Because List<T> implements IEnumerable<T>.But if you wrap it with a class and implement non-generic IEnumerable interface:
class NonGenericCollection : IEnumerable
{
public List<int> List { get; set; }
public NonGenericCollection()
{
List = new List<int>();
}
public IEnumerator GetEnumerator()
{
return List.GetEnumerator();
}
}
You can write any type you want and don't get any exceptions in compile time:
foreach(string x in new NonGenericCollection())
Because the return type is inferred as object and the actual type is not known until the run-time.

Finding an ObservableCollection for given/dynamic type via reflection

I have a Class with several ObservableCollections for different types. Now, I want to find the correct Collection for a given type via reflection, because I don't want to build an if-monster which I have to update every time I add another Collection.
This method was the first step:
public ObservableCollection<T> GetObservableCollectionForType<T>()
{
foreach (PropertyInfo info in this.GetType().GetProperties())
{
if (info.GetGetMethod() != null && info.PropertyType == typeof(ObservableCollection<T>))
return (ObservableCollection<T>)this.GetType().GetProperty(info.Name).GetValue(this, null);
}
return null;
}
Now, I need a second method, which accepts a concrete object as parameter and finds the correct Collection. Somehow like this:
public ObservableCollection<T> GetObservableCollectionFor(object sObject)
{
Type wantedType = sObject.GetType();
foreach (PropertyInfo info in this.GetType().GetProperties())
{
if (info.GetGetMethod() != null && info.PropertyType == ObservableCollection<wantedType>)
return this.GetType().GetProperty(info.Name).GetValue(this, null);
}
return null;
}
Any ideas how to realize this?
Update:
A working solution:
public object GetObservableCollectionFor(object sObject)
{
Type wantedType = sObject.GetType();
foreach (PropertyInfo info in this.GetType().GetProperties())
{
if (info.GetGetMethod() != null && info.PropertyType == typeof(ObservableCollection<>).MakeGenericType(new[]{wantedType}))
return this.GetType().GetProperty(info.Name).GetValue(this, null);
}
return null;
}
This will return the correct collection as object. I still don't know how to cast to the correct generic type, but casting to IList is enough for adding and removing.
When comparing the type of the property, it looks like you need to add a call to MakeGenericType() on the ObservableCollection type. Haven't tested this but maybe something like...
public ObservableCollection<T> GetObservableCollectionFor(object sObject)
{
Type wantedType = sObject.GetType();
foreach (PropertyInfo info in this.GetType().GetProperties())
{
if (info.GetGetMethod() != null && info.PropertyType == typeof(ObservableCollection<>).MakeGenericType(new[]{Type.GetType(wantedType)})
return (ObservableCollection<T>)this.GetType().GetProperty(info.Name).GetValue(this, null);
}
return null;
}
Edit
To avoid overhead boxing with value types, the above method definition could be improved by changing the parameter type from object to type T
public ObservableCollection<T> GetObservableCollectionFor(T sObject)

CssStyleCollection Class : access properties through reflection

How can I access CssStyleCollection class properties ( most of all I interesting its key-value collection) using reflection?
// this code runns inside class that inherited from WebControl
PropertyInfo[] properties = GetType().GetProperties();
//I'am not able to do something like this
foreach (PropertyInfo property in properties)
{
if(property.Name == "Style")
{
IEnumerable x = property.GetValue(this, null) as IEnumerable;
...
}
}
Here's the syntax for getting the Style property via reflection:
PropertyInfo property = GetType().GetProperty("Style");
CssStyleCollection styles = property.GetValue(this, null) as CssStyleCollection;
foreach (string key in styles.Keys)
{
styles[key] = ?
}
Note that CssStyleCollection doesn't implement IEnumerable (it implements the indexing operator), so you can't cast it to that. If you want to get an IEnumerable, you could extract the keys using styles.Keys, and the values:
IEnumerable<string> keys = styles.Keys.OfType<string>();
IEnumerable<KeyValuePair<string,string>> kvps
= keys.Select(key => new KeyValuePair<string,string>(key, styles[key]));

Casting List<object> to List<T> at runtime

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.

Categories