user2160375
user2160375

Reputation:

Cast collection of objects to collection of concrete class

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.

Upvotes: 0

Views: 1199

Answers (2)

Jeppe Stig Nielsen
Jeppe Stig Nielsen

Reputation: 62012

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();

Upvotes: 1

user2160375
user2160375

Reputation:

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.

Upvotes: 0

Related Questions