简体   繁体   中英

Convert IEnumerable to type that implements IEnumerable

GIVEN:

If you have the values:

  1. Type type
  2. IEnumerable enumerable

And the following conditions are met:

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

GENERAL QUESTION:

Is it possible to create an instance of type via reflection that contains all of the elements of enumerable ?

BACKGROUND:

Most of the types in System.Collections have a constructor like Example(ICollection) , and if type has a constructor like that it is simple and straightforward to do Activator.CreateInstance(type, enumerable) . For types like Dictionary<TKey, TValue> though, it is not that simple. The only solution I have thought of looks like this:

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

I would be more willing to accept this solution of KeyValuePair<TKey, TValue> implemented an interface which contained the properties Key and Value so you could say:

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

rather than relying on reflection to get the aforementioned property values.

This solution would only work for types within System.Collections or custom types that strongly adhere to the definitions of said types.

SPECIFIC QUESTION:

Is there a more elegant way of converting enumerable to the type of type that also could account for edge cases like MyCollection : ICollection , where the type definition is not known to us?

UPDATE:

Here is an example:

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
}

This is one of the few reasons left to use good old ArrayList .

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

The above creates a strongly typed array (named array ) of the concrete objects contained in any enumerable named ienumerable . An array, of course, implements ICollection .

This technique allows you to avoid Reflection figuring out which generic MethodInfo to create and invoke to create the array. The framework does it for you.

The requested solution should be able to handle any type of container, including the one bellow? It seems pretty difficult, not to say impossible. The only way to populate this container is through the method Populate , but how would a general solution know that?

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM