Преобразуйте IEnumerable в тип, который реализует IEnumerable - PullRequest
0 голосов
/ 01 апреля 2019

ДАНА:

Если у вас есть значения:

  1. Type type
  2. IEnumerable enumerable

И выполняются следующие условия:

  1. typeof(IEnumerable).IsAssignableFrom(type)
  2. enumerable.All(element => element.GetType() == type.GetElementType())

ОБЩИЙ ВОПРОС:

Можно ли с помощью отражения создать экземпляр type, который содержит все элементы enumerable?

ФОН:

Mostиз типов в System.Collections есть конструктор, подобный Example(ICollection), и если type имеет конструктор, подобный этому, то просто и просто сделать Activator.CreateInstance(type, enumerable).Для таких типов, как Dictionary<TKey, TValue>, все не так просто.Единственное решение, о котором я подумал, выглядит следующим образом:

var dictionary = (IDictionary) Activator.CreateInstance(type);
var elementType = enumerable.First().GetType();
var key = elementType.GetProperty("Key");
var value = elementType.GetProperty("Value");

foreach (var element in enumerable)
{
   dictionary.Add(key.GetValue(element), value.GetValue(element));
}

Я бы с большей готовностью принял это решение: KeyValuePair<TKey, TValue> реализовал интерфейс, который содержал свойства Key и Value, чтобы вы моглискажем:

var keyValuePair = (IKeyValuePair) element;
dictionary.Add(keyValuePair.Key, keyValuePair.Value);

вместо того, чтобы полагаться на отражение для получения вышеупомянутых значений свойств.

Это решение будет работать только для типов в System.Collections или пользовательских типов, которые строго придерживаются определенийуказанные типы.

СПЕЦИФИЧЕСКИЙ ВОПРОС:

Существует ли более элегантный способ преобразования enumerable в тип type, который также может учитывать такие крайние случаи, какMyCollection : ICollection, где определение типа нам неизвестно?

ОБНОВЛЕНИЕ:

Вот пример:

var original = new Dictionary<int, string>
{
   //values
};

var type = original.GetType();
var enumerable = original.AsEnumerable();

var copy = (Dictionary<int, string>) DoSomeMagic(type, enumerable);

object DoSomeMagic(Type type, IEnumerable enumerable)
{
   //Add magic here
}

Ответы [ 2 ]

0 голосов
/ 01 апреля 2019

Запрашиваемое решение должно быть в состоянии обрабатывать любой тип контейнера, в том числе один ниже? Это кажется довольно сложным, не сказать невозможным. Единственный способ заполнить этот контейнер - это метод Populate, но как общее решение узнает это?

public class TupleList<T1, T2, T3> : IEnumerable<Tuple<T1, T2, T3>>
{
    private List<Tuple<T1, T2, T3>> _list = new List<Tuple<T1, T2, T3>>();

    public void Populate(T1 item1, T2 item2, T3 item3)
    {
        _list.Add(Tuple.Create(item1, item2, item3));
    }

    public Tuple<T1, T2, T3> this[int index] { get => _list[index]; }

    public IEnumerator<Tuple<T1, T2, T3>> GetEnumerator()
    {
        return _list.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}
0 голосов
/ 01 апреля 2019

Это одна из немногих причин, по которой стоит использовать старый добрый ArrayList.

System.Array ConvertUnknownIEnumerableToArray(IEnumerable ienumerable, Type type)
{
    var list = new ArrayList();
    foreach (var i in ienumerable) list.Add(i);
    return list.ToArray(type);
}

Приведенное выше создает строго типизированный массив (с именем array) конкретных объектов, содержащихся в любом перечислимом с именем ienumerable.Массив, конечно, реализует ICollection.

Этот метод позволяет избежать рефлексии, выясняющей, какой универсальный MethodInfo создать и вызывать для создания массива.Фреймворк делает это за вас.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...