Consider, that I've the following method:
public T Resolve<T>()
{
var targetType = typeof(T);
if (targetType.IsGenericType
&& targetType.GetGenerictTypeDefinition() == typeof(IEnumerable<>))
{
List<object> collection = this.ResolveCollection(targetType);
return (T)(object)collection;
}
return (T)this.ResolveSingle(targetType);
}
Sample usage:
IEnumerable<IFoo> coll = myClass.Resolve<IEnumerable<IFoo>>();
It is obvious, that sample will throw exception of invalid cast, because of covariance - we cannot cast List<object> into IEnumerable<IFoo> despite collection contains implementations of IFoo only.
Is there any workaround for that problem when using reflection and non-generic methods? I don't want to change Resolve signature so I don't have generic type of item to use LINQ Cast.
It is going to be ugly. You can also call the Linq method Enumerable.Cast<> after "making" it, i.e. filling out the generic argument.
Here is an extension method:
public static TIEnumerable ToIEnumerable<TIEnumerable>(this IEnumerable<object> source)
{
var type = typeof(TIEnumerable);
if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(IEnumerable<>))
throw new ArgumentException("Wrong type arg: " + type, "TIEnumerable");
var methOpen = typeof(Enumerable).GetMethod("Cast");
var methConstructed = methOpen.MakeGenericMethod(type.GenericTypeArguments[0]);
return (TIEnumerable)methConstructed.Invoke(null, new object[] { source, });
}
(You could even extend the non-generic IEnumerable since Cast<> operates on that.)
Then the body of your if (in your question) could be:
List<object> collection = this.ResolveCollection(targetType);
return collection.ToIEnumerable<T>();
If you want eager iteration and returning a List<>, that is:
List<object> collection = this.ResolveCollection(targetType);
return collection.ToIEnumerable<T>()
.ToList();
Found workaround:
List<object> collection = this.ResolveCollection(targetType);
var itemType = targetType.GetGenericArguments()[0];
var listType = typeof(List<>).MakeGenericType(itemType);
var listInstance = Activator.CreateInstance(listType, new object[0]) as IList;
foreach (var instance in collection)
{
listInstance.Add(instance);
}
return (T)listInstance;
Then, casting works like a chram.
Related
I'm busy building my own property grid. One of the issues that I'm struggling with currently is dealing with collections. Specifically adding a new item to a collection.
I have an object called CollectionDataSource that is a List containing objects of type T.
I'm getting the type of T and creating a new T object as follows:
var type = CollectionDataSource.GetType().GetGenericArguments().Single();
var newItem = Activator.CreateInstance(type);
Now I need to add newItem to CollectionDataSource. The problem is CollectionDataSource is of type object. I've tried casting to IEnumerable and ICollection but those interfaces cannot add a new item.
How can I add newItem to CollectionDataSource?
UPDATE
See my answer below. All I had to do was cast the collection to IList. That did the trick for me
Why not cast it to a List in the first place?
var myObject = something as List<object>;
if (myObject != null)
{
// successfully cast
}
else
{
// cast failed
}
Or you if you know the type:
public static Object TryConvertTo<T>(string input)
{
Object result = null;
try
{
result = Convert.ChangeType(input, typeof(T));
}
catch
{
}
return result;
}
T toAdd = Convert.ChangeType((IItemsSource)Activator.CreateInstance(type), typeof(T));
Then create a simple Add method:
public void Add<T>(T toAdd) where T: class { _list.Add(toAdd); }
It seems like the answer is simple. All that I had to do was cast CollectionDataSource to (IList). Problem solved
var type = CollectionDataSource.GetType().GetGenericArguments().Single();
var newItem = Activator.CreateInstance(type);
var a = (IList)CollectionDataSource;
a.Add(newItem);
I have a method that receives an IList. Is there a way to get the Type of the items of the IList?
public void MyMethod(IList myList)
{
}
I have a class that you can associate an IList to, you can also add a NewItem function, but I would like to be able to add items with the default empty constructor in case the user didn't set the NewItem function.
How can I get the Type of the underlying items? I would know how to do it if it was an IList<T>, but I can't change the API, because I can receive any kind of collection that implements IList, only restriction, that is not enforced by code, is that all the items in the collections we receive are of the same type.
Since it's an IList, you'd first have to check if it is actually generic:
if (list.GetType().IsGenericType)
Console.WriteLine($"Is generic collection of {list.GetType().GenericTypeArguments[0]}");
else
Console.WriteLine("Is not generic");
For example, using
IList list = new List<string>();
would give Is generic collection of System.String, and
IList list = new ArrayList();
would give Is not generic
Here is a heuristic algorithm that you can start with. Most (if not all) generic list interfaces inherit from IEnumerable<T>, so you can check if the list implements IEnumerable<T>. If it doesn't, you check the type of the first element, assuming of course that the list would contain elements of the same type. If the list is empty, this method returns null.
public static Type HeuristicallyDetermineType(IList myList)
{
var enumerable_type =
myList.GetType()
.GetInterfaces()
.Where(i => i.IsGenericType && i.GenericTypeArguments.Length == 1)
.FirstOrDefault(i => i.GetGenericTypeDefinition() == typeof (IEnumerable<>));
if (enumerable_type != null)
return enumerable_type.GenericTypeArguments[0];
if (myList.Count == 0)
return null;
return myList[0].GetType();
}
you could do something like this have a List and in an additional List add the types.
IList<object> iListObj = new List<object>
{
1234,
"Harold Nelson",
false,
'A'
};
var typeList = new List<object>();
foreach (var item in iListObj)
{
typeList.Add(item.GetType());
}
If the types passed are non-generic than you may have a grabbag of types (e.g.):
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
namespace ConsoleApp1
{
public class Program
{
public static void Main(string[] args)
{
var list = new ArrayList();
list.Add("Hello");
list.Add(0);
var types = GetTypes(list);
foreach (var itemType in types)
{
Console.WriteLine(itemType.ToString());
}
Console.ReadLine();
}
public static HashSet<Type> GetTypes(IList list)
{
var types = new HashSet<Type>();
foreach (var item in list)
{
var newType = item.GetType();
if (!types.Contains(newType))
{
types.Add(newType);
}
}
return types;
}
}
}
You would have to make the GetTypes function more complicated to deal with the generic case also.
I have an extension method :
public static List<object> ToModelViewObjectList<ModelViewType>(this IEnumerable<object> source)
{
List<ModelViewType> destinationList = new List<ModelViewType>();
PropertyInfo[] ModelViewProperties = typeof(ModelViewType).GetProperties();
foreach (var sourceElement in source)
{
object destElement = Activator.CreateInstance<ModelViewType>();
foreach (PropertyInfo sourceProperty in sourceElement.GetType().GetProperties())
{
if (ModelViewProperties.Select(m => m.Name).Contains(sourceProperty.Name))
{
destElement.GetType().GetProperty(sourceProperty.Name).SetValue(destElement, sourceProperty.GetValue(sourceElement));
}
}
destinationList.Add((ModelViewType)destElement);
}
return destinationList.Cast<object>().ToList();
}
And I have a method with a list of object that I want call extension methods in this method :
public void GridModel(IEnumerable<object> GridDataSource)
{
List<object> list = GridDataSource.ToModelViewObjectList<GridDataSource[0].GetType()>();
}
What should I write instead of GridDataSource[0].GetType() ?
Edited:
I have a method with a object parameter. I want to create a generic list of object type.
public void CreateListByNonGenericType(object myObject)
{
Type objType = myObject.GetType();
var lst = System.Collections.Generic.List < objType.MakeGenericType() > ();
}
What should I write instead of objType.MakeGenericType() ?
What should I write instead of GridDataSource[0].GetType() ?
Because you do not know the type at compile time, you have to perform everything in runtime. So you need to make a generic method from a method definition and call it. Something like following:
GridModel(IEnumerable<object> GridDataSource)
{
Type dataSourceElementType = GridDataSource[0].GetType();
//MyExtension is a type where ToModelViewObjectList defined
MethodInfo methodDefinition = typeof(MyExtension).GetMethod("ToModelViewObjectList");
//https://msdn.microsoft.com/en-us/library/system.reflection.methodinfo.makegenericmethod
MethodInfo method = methodDefinition.MakeGenericMethod(dataSourceElementType)
//https://msdn.microsoft.com/en-us/library/a89hcwhh
List<object> list = (List<object>)method.Invoke(GridDataSource, null);
}
But what you really want to do is to accept a type to ToModelViewObjectList as an argument instead of generic type parameter, like following, because you do not use ModelViewType outside of the function, as you return list of objects. Also, it will make your code clear.
public static List<object> ToModelViewObjectList(this IEnumerable<object> source, Type modelViewType)
{
List<object> destinationList = new List<object>();
PropertyInfo[] ModelViewProperties = modelViewType.GetProperties();
foreach (var sourceElement in source)
{
object destElement = Activator.CreateInstance(modelViewType);
foreach (PropertyInfo sourceProperty in sourceElement.GetType().GetProperties())
{
if (ModelViewProperties.Select(m => m.Name).Contains(sourceProperty.Name))
{
destElement.GetType().GetProperty(sourceProperty.Name).SetValue(destElement, sourceProperty.GetValue(sourceElement));
}
}
destinationList.Add(destElement);
}
return destinationList;
}
And so you can call this extension method almost like you intended:
List<object> list = GridDataSource.ToModelViewObjectList(GridDataSource[0].GetType());
Also, it would be good to get the element type of GridDataSource using something else than getting first element's type.
public void CreateListByNonGenericType(object myObject)
{
Type objType = myObject.GetType();
Type listType = typeof(List<>).MakeGenericType(objType);
//We can not use here generic version of the interface, as we
// do not know the type at compile time, so we use the non generic
// interface IList which will enable us to Add and Remove elements from
// the collection
IList lst = (IList)Activator.CreateInstance(listType);
}
If you know your parameter is of "object" type only you can create list of "object" type. At the time of using list cast it to specific type like this.
public void CreateListByNonGenericType(object myObject)
{
object objType = myObject.GetType();
var lst = new List<object>();
lst.Add(myObject);
}
But If you know the type of object at before calling the method you can use Generic method like this.
public void CreateListByNonGenericType<T>(T myObject, string s)
{
var lst = new List<T>();
lst.Add(myObject);
}
I'm trying to make a model reflection tool. I have come a long way so far but now i'm stuck.
I have this
public static void RenderModelList(List<T> modelList)
{
foreach (T model in modelList)
{
PropertyInfo[] properties = model.GetType().GetProperties();
foreach (PropertyInfo property in properties)
{
object propValue = property.GetValue(model, null);
//Check if the property is a collection and do recursion
if (propValue != null)
{
if (isCollection(propValue))
{
//This only works for Lists of the same <T>
List<T> li = Convert.ChangeType(propValue, propValue.GetType()) as List<T>;
if (li != null)
{
if (li.Count > 0)
{
RenderModelList(li, loop);
}
}
else
{
//Its another type what to do?
// Create a List<> of unknown type??
}
}
}
}
}
}
My problem is that if I pass this method a List<Persons> and the Person has a property which is a List<Cars> - I can't use Convert.ChangeType - because this is not the T.
So how do I loop thrugh a "List" and get access to the properties of this object ?
It seems to me that your method can be a lot more loosely typed:
public static void RenderModelList(IEnumerable list)
{
foreach (object model in list)
{
...
}
}
Then you just need to cast to IEnumerable, not a specific sequence or list type.
Well, your method should not rely on T. You can just use IEnumerable instead (not IEnumerable<T>, because it again depends on T). Note that every List<T> implements IEnumerable, so your method will work with them; however, other collections often implement IEnumerable as well. This may or may not be what you need.
If you choose the proposed way, your test isCollection will be like this:
IEnumerable propValueAsEnumerable = propValue as IEnumerable;
if (propValueAsEnumerable != null)
RenderModelList(propValueAsEnumerable);
I would perhaps refactor your method into something like that:
IEnumerable<object> GetPropertyValuesFlat(object o)
{
return o.GetType()
.GetProperties()
.Select(pi => pi.GetValue(o, null))
.Where(pv => pv != null)
.SelectMany(pv => pv is IEnumerable<object> ?
(IEnumerable<object>)pv : new[] {pv});
}
//...
foreach (object p in GetPropertyValuesFlat(o))
render(p);
(caution: not tested)
Edit: well, won't work as SelectMany doesn't understand non-generic IEnumerable. Changed it to work with IEnumerable<object>. At least, each IEnumerable<T> is an IEnumerable<object> with class T.
What would be the best way of getting the type of items a generic list contains? It's easy enough to grab the first item in the collection and call .GetType(), but I can't always be sure there will be an item in the collection.
Hope that makes sense.
Thanks,
Sonny
You could use the Type.GetGenericArguments method for this purpose.
List<Foo> myList = ...
Type myListElementType = myList.GetType().GetGenericArguments().Single();
For a more robust approach:
public static Type GetListType(object someList)
{
if (someList == null)
throw new ArgumentNullException("someList");
var type = someList.GetType();
if (!type.IsGenericType || type.GetGenericTypeDefinition() != typeof(List<>))
throw new ArgumentException("Type must be List<>, but was " + type.FullName, "someList");
return type.GetGenericArguments()[0];
}
But if your variable is typed List<T> then you can just use typeof(T). For example:
public static Type GetListType<T>(List<T> someList)
{
return typeof(T);
}
Note that you don't really even need the someList parameter. This method is just an example for how you could use typeof if you are already in a generic method. You only need to use the reflection approach if you don't have access to the T token (the list is stored in a non-generic-typed variable, such as one typed IList, object, etc.).
list.GetType().GetGenericArguments()[0]
Here's another way which works for non-generic collections, too:
static Type GetItemType(Type collectionType)
{
return collectionType.GetMethod("get_Item").ReturnType;
}
That is, get the return type of foo[x], where foo is of the specified type.
Examples:
// Generic type; prints System.Int32
Console.WriteLine(GetItemType(typeof(List<int>)));
// Non-generic type; prints System.String
Console.WriteLine(GetItemType(typeof(System.Collections.Specialized.StringCollection)));
The GetItemType method above has a couple issues, though:
It throws a NullReferenceException if the type has no indexing operator.
It throws an AmbiguousMatchException if the type has multiple overloads for the indexing operator (e.g. this[string] and this[int]).
Here is a more refined version:
public static Type GetItemType(this Type collectionType)
{
var types =
(from method in collectionType.GetMethods()
where method.Name == "get_Item"
select method.ReturnType
).Distinct().ToArray();
if (types.Length == 0)
return null;
if (types.Length != 1)
throw new Exception(string.Format("{0} has multiple item types", collectionType.FullName));
return types[0];
}
What about this, its all static (e.g. no instances required), and fast (no loops, no usage of linq), and it is simple :) these work for collections:
[System.Diagnostics.DebuggerHidden]
public static Type GetIndexedType(this ICollection poICollection)
{
PropertyInfo oPropertyInfo = poICollection == null ? null : poICollection.GetType().GetProperty("Item");
return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
}
[System.Diagnostics.DebuggerHidden]
public static Type GetEnumeratedType(this ICollection poICollection)
{
PropertyInfo oPropertyInfo = poICollection == null ? null : poICollection.GetType().GetMethod("GetEnumerator").ReturnType.GetProperty("Current");
return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
}
And a few simple unit tests:
[Test]
public void GetIndexedType()
{
Assert.AreEqual(null, ((ICollection)null).GetIndexedType());
Assert.AreEqual(typeof(int), (new List<int>()).GetIndexedType());
Assert.AreEqual(typeof(bool), (new SortedList<string, bool>()).GetIndexedType());
}
[Test]
public void GetEnumeratedType()
{
Assert.AreEqual(null, ((ICollection)null).GetEnumeratedType());
Assert.AreEqual(typeof(int), (new List<int>()).GetEnumeratedType());
Assert.AreEqual(typeof(KeyValuePair<string, bool>), (new SortedList<string, bool>()).GetEnumeratedType());
}
Notice the fact that there are two ways to look at this, one type may be returned by the indexer and an other type may be returned by the enumerator. The unit test do show both.
Have fun,
Frans.
P.s. For enumerables:
[System.Diagnostics.DebuggerHidden]
public static Type GetEnumeratedType(this System.Collections.IEnumerable poIEnumerable)
{
PropertyInfo oPropertyInfo = poIEnumerable == null ? null : poIEnumerable.GetType().GetMethod("GetEnumerator").ReturnType.GetProperty("Current");
return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
}
And for enumerator:
[System.Diagnostics.DebuggerHidden]
public static Type GetEnumeratedType(this System.Collections.IEnumerator poIEnumerator)
{
PropertyInfo oPropertyInfo = poIEnumerator == null ? null : poIEnumerator.GetType().GetProperty("Current");
return oPropertyInfo == null ? null : oPropertyInfo.PropertyType;
}
public Type GetType(IEnumerable<object> resultList)
{
return resultList.GetType().GetElementType();
}
Old question new method with dynamic
void Foo(){
Type type GetTypeT(data as dynamic);
}
private static Type GetTypeT<T>(IEnumerable<T> data)
{
return typeof(T);
}
Public Shared Function ListItemType(ListType As System.Type) As System.Type
If Not ListType.IsGenericType Then
If ListType.BaseType IsNot Nothing AndAlso ListType.BaseType.IsGenericType Then
Return ListItemType(ListType.BaseType)
End If
Else
Return ListType.GetGenericArguments.Single
End If
End Function
Here is a solution that also works with derived classes.
Because with this class :
public class SubList : List<int>
{ }
If you call : subList.GetType().GetGenericArguments().Single()
It will throws a System.InvalidOperationException
With this method it works for derived classes :
public Type GetListItemType<T>(List<T> list)
{
Type type = list.GetType();
while (type != typeof(List<T>))
type = type.BaseType;
return type.GetGenericArguments().Single();
}
var list = new List<int>();
var subList = new SubList();
Console.WriteLine(GetListItemType(list)); // System.Int32
Console.WriteLine(GetListItemType(subList)); // System.Int32