Как преобразовать IList неизвестного типа (тип известен во время выполнения) в массив? - PullRequest
0 голосов
/ 20 октября 2019

Я использую отражение для копирования объекта любого пользовательского класса во время выполнения. Я использую FieldInfo, чтобы получить все поля и затем правильно скопировать их в зависимости от их типа.

Единственный тип, с которым я могу работать в начале алгоритма копирования, это System.Object (AKA object),Я делаю много проверок типов. Поэтому, когда мой метод проверки говорит, что этот конкретный объект является неким простым одномерным массивом, это, несомненно, массив. Однако я могу получить доступ к типу элементов в этом массиве только во время выполнения.

Я успешно скопировал List<type known at runtime> следующим образом:

public object Get_ListCopy(object original)
{
    Type elementType = original.GetType().GetGenericArguments()[0];
    Type listType = typeof(List<>).MakeGenericType(elementType);

    object copy = Activator.CreateInstance(listType);
    var copyIList = copy as IList;

    foreach (var item in original as IEnumerable)
        copyIList.Add(item);

    copy = copyIList;

    return copy;
}

Затем я попытался переписать метод дляпростой массив:

public object Get_ArrayCopy(object original)
{
    Type elementType = original.GetType().GetElementType();    // difference here
    Type listType = typeof(List<>).MakeGenericType(elementType);

    object copy = Activator.CreateInstance(listType);
    var copyIList = copy as IList;

    foreach (var item in original as IEnumerable)
        copyIList.Add(item);

    copy = Enumerable.Range(0, copyIList.Count).Select(i => copyIList[i]).ToArray();    // difference here

    return copy;
}

Но это возвращает исключение при присваивании значения полю с использованием FieldInfo.SetValue(copyObject, convertedValue) // where convertedValue is object copy from the method above:

System.ArgumentException: 'Object of type 'System.Object[]' cannot be converted to type 'System.Int32[]'.'

Для этого конкретного примера массив выглядел так:

public int[] Array = { 1, 2, 3 };

И последнее: я знаю, как решить эту проблему, используя универсальные методы, и MethodInfo ...MakeGenericMethod(...).Invoke, я просто подумал, что этого можно избежать (возможно, я ошибаюсь). Также сериализация не может быть использована.

1 Ответ

2 голосов
/ 20 октября 2019

Ваша строка

copy = Enumerable.Range(0, copyIList.Count)
    .Select(i => copyIList[i])
    .ToArray();

На самом деле:

copy = Enumerable.Range(0, copyIList.Count)
    .Select<int, object>(i => copyIList[i])
    .ToArray<object>();

Компилятор знает, что copyIList - это IList. Когда вы делаете copyIList[i], вы получаете доступ к индексатору IList , который возвращает object. Таким образом, Select возвращает IEnumerable<object>, а ToArray() генерирует object[].

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

((Array)original).Clone();

В противном случае вы можете создать новый массив с помощью Array.CreateInstance.


В случае со списком было бы проще использовать конструктор List, который принимаетдругой список для копирования:

public object Get_ListCopy(object original)
{
    Type elementType = original.GetType().GetGenericArguments()[0];
    Type listType = typeof(List<>).MakeGenericType(elementType);

    object copy = Activator.CreateInstance(listType, new[] { original });
    return copy;
}
...