Копировать массив объектов в массив другого типа

Раньше я сталкивался с проблемой при попытке поделиться определением типа между моим веб-сервисом ASMX и моей страницей .aspx (веб-клиент).

Запутался в C# Массив объектов и неявное преобразование типов

Насколько я понимаю совет, «проблема», которую это создает, может быть решена путем копирования массива объектов, созданных в клиенте, в новый массив объектов, как определено прокси-классом ASMX.

Будучи новичком в C #, я все еще борюсь с этой простой задачей. Вот еще части моего кода (остальные фрагменты в предыдущем посте остались без изменений):

... здесь я заполняю «тестовые данные», которые хочу передать веб-службе:

// create an array of MetaData objects
MetaData[] nvPairs = new MetaData[20];   // arbitrary length of 20 pairs

// create arbitrary MetaData objects in the array
nvPairs[0] = new MetaData("Grant Number", "2577-9912");
nvPairs[1] = new MetaData("OPEAnalyst", "Simpson");

... здесь я пытаюсь выполнить функцию "копирования" из "реального" типа, определенного в моем пространстве имен TRIMBrokerUtil (которое я не могу полностью использовать из-за прокси-сервера), в прокси-версию этого типа:

protected TRIMBrokerASMXProxy.ASMXProxy.MetaData[] CopyMetaData(
    MetaData utilArray)
{
    TRIMBrokerASMXProxy.ASMXProxy.MetaData[] outArray = 
        new TRIMBrokerASMXProxy.ASMXProxy.MetaData[utilArray.Name.Length];
    int i;
    for (i = 0; i < utilArray.Name.Length; i++)
    {
        outArray[i].Name = utilArray.Name;
        outArray[i].Value = utilArray.Value;
    }
    return outArray;
}

... а затем здесь я пытаюсь вызвать эту функцию (компилятор отмечает 2 ошибки в этой строке:

TRIMBrokerASMXProxy.ASMXProxy.MetaData[] kvData = 
    CopyMetaData(metaDataArray); 

Обе ошибки компиляции ниже указывают на одну и ту же строку:

Ошибка 1 Лучший перегруженный метод, соответствующий '_Default.CopyMetaData(TRIMBrokerUtil.MetaData)', имеет недопустимые аргументы.

Ошибка 2. Аргумент «1»: невозможно преобразовать «TRIMBrokerUtil.MetaData[]» в «TRIMBrokerUtil.MetaData».

Я близко?


person John Adams    schedule 24.03.2009    source источник


Ответы (3)


Вы объявили свой параметр равным MetaData, а не MetaData[], другими словами, это не массив. Затем вы используете utilArray.Name довольно часто, но неясно, почему.

Я подозреваю, что вы действительно хотите:

protected TRIMBrokerASMXProxy.ASMXProxy.MetaData[]
    CopyMetaData(MetaData[] utilArray)
{
    TRIMBrokerASMXProxy.ASMXProxy.MetaData[] outArray = 
        new TRIMBrokerASMXProxy.ASMXProxy.MetaData[utilArray.Length];
    for (int i = 0; i < utilArray.Length; i++)
    {
        outArray[i] = new TRIMBrokerASMXProxy.ASMXProxy.MetaData();
        outArray[i].Name = utilArray[i].Name;
        outArray[i].Value = utilArray[i].Value;
    }
    return outArray;
}

Кстати, вы можете рассмотреть директиву using, чтобы упростить чтение:

using ProxyMetaData = TRIMBrokerASMXProxy.ASMXProxy.MetaData;

...

protected ProxyMetaData[] CopyMetaData(MetaData[] utilArray)
{
    ProxyMetaData[] outArray = new ProxyMetaData[utilArray.Length];
    for (int i = 0; i < utilArray.Length; i++)
    {
        outArray[i] = new ProxyMetaData();
        outArray[i].Name = utilArray[i].Name;
        outArray[i].Value = utilArray[i].Value;
    }
    return outArray;
}

Другой вариант — Array.ConvertAll:

ProxyMetaData[] output = Array.ConvertAll(input,
    metaData => new ProxyMetaData(metaData.Name, metaData.Value));

Если вы не используете С# 3, вы можете использовать для этого анонимный метод. Если у ProxyMetaData нет соответствующего конструктора и вы используете C# 3, вы можете использовать инициализатор объекта:

ProxyMetaData[] output = Array.ConvertAll(input,
    metaData => new ProxyMetaData { metaData.Name, metaData.Value });

Если вы застряли с С# 2 и не имеете подходящего конструктора, то:

ProxyMetaData[] output = Array.ConvertAll(input, delegate(MetaData metaData)
{
    ProxyMetaData proxy = new ProxyMetaData();
    proxy.Name = metaData.Name;
    proxy.Value = metaData.Value;
});

Я думаю, что покрыл все основы :)

person Jon Skeet    schedule 24.03.2009
comment
Да здравствует король Джон..+1. Небольшой вопрос: мне нравится использовать List‹T›, а затем возвращаться с помощью ToArray(). Мне нравится использовать списки, так как я не люблю использовать индексы, такие как i. Я убиваю производительность, делая это? - person Perpetualcoder; 24.03.2009
comment
Ну, вы снижаете производительность, но, вероятно, незначительно. Если я конвертирую из массива в массив, я, вероятно, не буду использовать список, но как только возникнут какие-либо сомнения относительно длины конечного результата, я буду использовать список для удобства. . Не забывайте Array.ConvertAll, хотя :) - person Jon Skeet; 24.03.2009
comment
Ух ты!! Где баночка с чаевыми! Ты святой и так хорош, что помогаешь мне вот так. Я учусь медленно, и я действительно благодарю вас за вашу любезную помощь. - person John Adams; 24.03.2009
comment
К сожалению, проблемы с компиляцией, описанные выше, были преодолены, и теперь я не могу создать прокси-класс из-за этого: TRIMBrokerUtil.MetaData не может быть сериализован, поскольку у него нет конструктора без параметров. - person John Adams; 24.03.2009
comment
Тогда это просто - дайте ему конструктор без параметров :) - person Jon Skeet; 24.03.2009
comment
Опять же, не уверен. Вот что я написал: namespace TRIMBrokerUtil { public class MetaData { protected string _Name; защищенная строка _Value; общедоступные метаданные (строковое ключевое слово, строковое значение) { this.Name = ключевое слово; this.Value = настройка; } - person John Adams; 24.03.2009
comment
Вам также нужен публичный MetaData() {} - это конструктор без параметров. - person Jon Skeet; 25.03.2009
comment
Спасибо. Добавление этого сработало, но я не понимаю волшебства всего этого. Сериализация имеет дело с преобразованием сообщений в XML, и моему маленькому объекту нужны ключ и значение для конструктора, поэтому я как будто добавляю что-то, что на самом деле не используется. Но это работает! Еще раз спасибо, Джон. - person John Adams; 25.03.2009

Я бы просто использовал LINQ для этого:

TRIMBrokerASMXProxy.ASMXProxy.MetaData[] kvData =
    metaDataArray.Select(d => 
        new TRIMBrokerASMXProxy.ASMXProxy.MetaData(
            d.Name, d.Value)).ToArray();

Кроме того, если вы используете .NET 3.5, это означает, что вы также можете использовать WCF, который вы должны использовать для создания прокси. Вы сможете атрибутировать свой тип TRIMBrokerASMXProxy.ASMXProxy.MetaData с помощью атрибута DataContract и членов, сериализуемых с помощью атрибута DataMember. Затем вы сможете определить свой контракт с фактическим типом и вообще не выполнять преобразование.

person casperOne    schedule 24.03.2009
comment
Я бы тоже, но я не уверен, что предложил бы это новичку в С#, особенно когда мы не знаем, использует ли он .NET 3.5 :) - person Jon Skeet; 24.03.2009
comment
Я только что закончил вводить тот же самый код, прежде чем SO подсказал, что был опубликован новый ответ :) +1 - person Perpetualcoder; 24.03.2009
comment
@Джон, может быть, даст новичку мотивацию, чтобы он быстро разбирался в вещах :) - person Perpetualcoder; 24.03.2009
comment
@Jon: В этом прелесть двух отдельных ответов, мы можем позволить новичку (ваше обозначение, а не мое) решить. - person casperOne; 24.03.2009
comment
Не мое назначение - OP: будучи новичком в C #, я все еще борюсь с этой простой задачей. Я расширил свой ответ другой возможностью, которая немного более эффективна, чем обычный LINQ (и в .NET 2.0) - Array.ConvertAll. - person Jon Skeet; 24.03.2009
comment
@Jon: В конце концов, это плохой код, не должно быть преобразования между типом прокси и исходным типом, поскольку эти типы должны быть в отдельной сборке и совместно использоваться сервером и клиентом. Код преобразования — это код, который никогда не нужно писать. - person casperOne; 24.03.2009
comment
Да, где это возможно. Прошло много времени с тех пор, как я написал веб-службу .NET, поэтому я не чувствовал, что это мое дело, чтобы сделать этот звонок :) - person Jon Skeet; 24.03.2009

Вы также можете использовать Array.ConvertAll. Я знаю, что вы относительно новичок в этом, поэтому позвольте мне попытаться объяснить. Он имеет 2 общих параметра. Первый — это тип массива, который он хочет преобразовать (давайте назовем его I). И второй тип, в который вы хотите преобразовать (давайте назовем его O). Он принимает массив типа I и возвращает массив типа O. Второй параметр — это делегат Converter. Применив наименование, мы имеем его сигнатуру.

delegate O Converter(I input);

Тело делегата должно содержать код, необходимый для преобразования. Внутри функции ConvertAll код перебирает каждое значение во входном массиве и затем передает делегату. Значение, возвращаемое делегатом, затем сохраняется в выходной массив. Выходной массив возвращается пользователю после преобразования всех значений.

using ProxyMetaData = TRIMBrokerASMXProxy.ASMXProxy.MetaData;

ProxyMetaData[] convertedArray = Array.ConvertAll<MetaData, ProxyMetaData>(utilArray, 
delegate(MetaData metaData)
{
    ProxyMetaData returnValue = new ProxyMetaData();
    returnValue.Name = metaData.Name;
    returnValue.Value = metaData.Value;
    return returnValue;
});
person jake.stateresa    schedule 24.03.2009
comment
Спасибо, Джейк. Это имеет смысл, и я попробую, но с методом Джона я столкнулся с еще одним поворотом, пытаясь перестроить мой прокси-класс: TRIMBrokerUtil.MetaData не может быть сериализован, потому что у него нет конструктора без параметров. Но мне нужен конструктор с 2 параметрами; 1 для имени, 1 для значения - person John Adams; 25.03.2009
comment
Если я правильно понимаю, я бы столкнулся с той же жалобой, используя Array.ConvertAll. Я думаю, что я столкнулся с более серьезной проблемой, возможно?? - person John Adams; 25.03.2009