Как преобразовать один тип в другой, используя отражение? - PullRequest
1 голос
/ 20 марта 2011

У меня есть два типа, которые очень похожи (то есть имена членов очень похожи).

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

Обновление

Вот пример кода:

main()
{
  FromCsvFile x = new FromCsvFile(fileName);
  OptionsEnt y = x.ToOptionsEnt(); // See helper function below.
}

// Chained helper function to convert type "FromCsvFile" to type "OptionsEnt".
// Want to replace this with something more elegant (perhaps with reflection?).
// Notice the corner cases, i.e. the "ExpirationDate" is a special conversion.
public static OptionsEnt ToOptionsEnt(this FromCsvFile fromCsvFile)
{
  return new OptionsEnt
           {
             Last = fromCsvFile.Last,
             Ask = fromCsvFile.Ask,
             Bid = fromCsvFile.Bid,
             Delta = fromCsvFile.Delta,
             EODsnapshotNewYorkTime = fromCsvFile.EODsnapshotNewYorkTime,
             Exchange = fromCsvFile.Exchange,
             ExpirationDate = fromCsvFile.Expiration.ToTypeIceDate(),
             Gamma = fromCsvFile.Gamma,
             IV = fromCsvFile.IV,
             LastDate = fromCsvFile.Date.ToTypeIceDate(),
             AdjustedStockClose = fromCsvFile.AdjustedStockClose,
             MeanPrice = fromCsvFile.MeanPrice,
             OptionType = fromCsvFile.OptionType == "C" ? OptionTypeEnum.enCall : OptionTypeEnum.enPut,
             OpenInterest = fromCsvFile.OpenInterest,
             Rho = fromCsvFile.Rho,
             StockSymbol = fromCsvFile.SymbolStock,
             StrikePrice = fromCsvFile.StrikePrice,
             Symbol = fromCsvFile.Symbol,
             StockPriceForIV = fromCsvFile.StockPriceForIV,
             Star = fromCsvFile.Star,
             Theta = fromCsvFile.Theta,
             Vega = fromCsvFile.Vega,
             Volume = fromCsvFile.Volume,
             IVnotInterpolated = fromCsvFile.IVnotInterpolated
          };
}

Обновление

Решили перейти с AutoMapper.

Вот код, который заменяет все кода выше (при условии, что все имена членов имеют одинаковые имя и тип):

main()
{
  FromCsvFile x = new FromCsvFile(fileName);
  OptionsEnt y = Mapper.Map<FromCsvFile, OptionsEnt>(x);
}

Поскольку нам нужны некоторые пользовательские конвертеры (то есть DateTime >> IceDateTime), вот дополнительная строка кода, которая включает в себя пользовательское отображение для параметра «ExpirationDate». Добавление этой строки позволяет избежать возникновения исключения, поскольку оно не знает, как преобразовать даты из одного формата в другой.

 Mapper.CreateMap<DateTime, typeIceDate>().ConvertUsing(ConverterIceTypeIceDate.ToTypeIceDate);

Ответы [ 3 ]

3 голосов
/ 20 марта 2011

Может быть Automapper ?

Например:

Mapper.CreateMap<FromCsvFile, OptionsEnt >();
return Mapper.Map<FromCsvFile, OptionsEnt>(fromCsvFile);
2 голосов
/ 20 марта 2011

Используйте для этого что-то вроде AutoMapper . Это позволит вам просто определить, что класс OptionsEnt должен быть сопоставлен с FromCsvFile, и если у них есть свойства с одинаковыми именами и типами, вам не нужно определять что-либо еще.

В противном случае вам придется выполнять итерацию по свойствам.

0 голосов
/ 20 марта 2011

См. Копируемый: платформа для копирования или клонирования объектов .NET .Он немного медленнее (использует отражение), но у него есть одно преимущество: вы можете изменить источник для обработки угловых случаев, когда переменные-члены нуждаются в небольшой работе для преобразования.

Например, в примере исходного кода в вопросе переменная-член "ExpirationDate" имеет тип "DateTime" в одном типе и тип "IceDateTime" в другом (необходимо преобразовать формат даты с помощьюметод расширения .ToDateTime).

Вот источник (см. оригинальную запись в блоге для получения дополнительной информации):

// Modification to original source code.
Type type = instance.GetType();

if (instance.GetType().Name == "DataTable")
{
    // Added to handle custom type.
    DataTable dt = (DataTable)instance;
    copy = dt.Copy();
}
else if (instance.GetType().Name == "DataSet")
{
    // Added to handle custom type.
    DataSet ds = (DataSet)instance;
    copy = ds.Copy();
}
else
{
    // This is the original source.
    while (type != null)
    {
        foreach (FieldInfo field in type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
        {
            object value = field.GetValue(instance);
            if (visited.ContainsKey(value))
                field.SetValue(copy, visited[value]);
            else
                field.SetValue(copy, value.Clone(visited));
        }
        type = type.BaseType;
    }
}
return copy;
...