2014-12-05 5 views
0

Считают, что я имею следующий метод: использованиеколлекция Cast объектов в коллекции конкретного класса

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

Пример:

IEnumerable<IFoo> coll = myClass.Resolve<IEnumerable<IFoo>>(); 

Очевидно, что образец выбросит исключение invalid cast, из-за ковариации - мы не можем отличить List<object> в IEnumerable<IFoo>, несмотря на то, что collection содержит только версии IFoo. Есть ли какое-либо обходное решение для этой проблемы при использовании рефлексивных и не общих методов? Я не хочу менять подпись Resolve, поэтому у меня нет общего типа элемента для использования LINQ Cast.

+2

Как определяется 'ResolveCollection'? Почему он возвращает 'List ' вместо типизированной коллекции? – MarcinJuraszek

+0

'ResolveCollection' не является универсальным, поэтому он не может возвращать коллекцию конкретных классов. Учтите, что 'ResolveCollection' является внешним, поэтому его нельзя изменить или просмотреть источник. Требуется только 'Type', где' Type' является 'IEnumerable' и возвращает' List' с несколькими экземплярами типа общего аргумента 'IEnumerable'. –

+0

Что-то странное в вашем состоянии 'if'. Вы имели в виду 'targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof (IEnumerable <>)'? –

ответ

0

Найдено решение:

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; 

Затем, литье работает как chrám.

1

Это будет уродливо. Вы также можете вызвать метод Linq Enumerable.Cast<> после его «создания», т. Е. Заполнить общий аргумент.

Вот метод расширения:

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, }); 
} 

(Вы можете даже продлить необщего IEnumerable поскольку Cast<> работает на этом.)

Тогда тело вашего if (ваш вопрос) может быть:

List<object> collection = this.ResolveCollection(targetType); 

    return collection.ToIEnumerable<T>(); 

Если вы хотите нетерпеливые итерации и возвращение List<>, то есть:

List<object> collection = this.ResolveCollection(targetType); 

    return collection.ToIEnumerable<T>() 
     .ToList();