2013-06-14 6 views
27

Если у меня есть динамический объект или анонимный объект, структура которого точно соответствует структуре строго типизированного объекта, существует ли способ .NET для создания типизированного объекта из динамического объекта?Есть ли способ конвертировать динамический или анонимный объект в строго типизированный объявленный объект?

Я знаю, что я могу использовать тип типа LINQ dynamicList.Select(dynamic => new Typed { .... }, или я могу использовать Automapper, но мне интересно, нет ли чего-то специально для этого?

ответ

35

Вы можете сериализовать в промежуточный формат, просто десериализации этого права после , Это не самый элегантный и эффективный способ, но это могло бы сделано ваша работа:

Пусть это ваш класс:

// Typed definition 
class C 
{ 
    public string A; 
    public int B; 
} 

И это ваш анонимный экземпляр:

// Untyped instance 
var anonymous = new { 
    A = "Some text", 
    B = 666 
}; 

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

var serializer = new System.Web.Script.Serialization.JavaScriptSerializer(); 
var json = serializer.Serialize(anonymous); 
var c = serializer.Deserialize<C>(json); 

Обратите внимание, что это теоретически возможно с любой сериализатора/десериализации (XmlSerializer, бинарной сериализации, другие JSON ЛИЭС), до тех пор, как в обе стороны синхронно.

+0

+1. Приятная мысль, хотя она использует отражение за кулисами. – Tengiz

+2

Продумали это: я думаю, у вас всегда будет промежуточное представление, когда вы это сделаете, и у вас всегда будет отражение на обоих концах (анонимный и типизированный объект). Поэтому я думаю, чтобы оптимизировать это, вам просто нужно найти наиболее эффективную промежуточную форму: http://www.servicestack.net/benchmarks/ –

1

Если ваш объект наследуется от MarshalByRefObject, вы можете использовать RealProxy.

Другим вариантом является использование Reflection, но вы можете быть ограничены материалами, не отмеченными виртуальными и/или использующими интерфейсы. Вы также можете скопировать значения, считая, что свойства доступны для записи, и пустой конструктор работает для вашего дела.

Фактический ответ на ваш вопрос - нет, нет автоматического способа обработки динамического объекта как определенного типа, если он не является экземпляром этого типа, и нет никакого автоматического средства для копирования значений из динамического/анонимный объект в экземпляр именованного класса.

Время выполнения не имеет представления о том, что происходит в конструкторе, или о том, как класс реализован внутри, поэтому любое такое средство выведет из системы безопасность типа. Весь смысл динамического состоит в том, чтобы разрешить отправку/укладку утки/etc.

Редактировать: Если я неправильно понял вопрос, дайте мне знать, но я предполагаю, что вы хотите рассматривать динамический объект, как если бы это был экземпляр SomeType.

В моих собственных проектах я использовал класс Object Mapper для этого, где он соответствует именам свойств для доступных для записи свойств и одинаковых или принудительных типов, поэтому, по крайней мере, мне не пришлось писать 10 000 строк шаблона, хотя мой источник не был динамическим, поэтому я использовал Reflection.Emit/DynamicMethod, чтобы значительно ускорить его.

+0

Вы меня немного неправильно поняли. Я не хочу автоматически обрабатывать динамический объект как статически типизированный объект. Я хочу преобразовать динамический объект в статически типизированный объект. Варианты использования, вероятно, будут в основном для динамических DTO и т. Д., Поэтому логика конструктора не такая большая, как может быть, фактор риска. – ProfK

+0

Хорошо, простите об этом; нет автоматического способа сделать это. Решение jherax работает для преобразования в словарь, но отражение будет медленным. В моем сценарии я создал ObjectMapper, который кэширует DynamicMethod (построенный с использованием испускаемого IL), который копирует между типами, поэтому он очень быстро после первого использования, но у нас есть тонна типов DTO. Для динамического объекта, который не будет работать, хотя есть некоторые потенциальные ускорения, если вам это нужно. – russbishop

10

Ваш вопрос сводится к вопросу: можно преобразовать один статически типизированных переменную в другую статически типизированных переменной (из различных наследственных цепей)? и ответ, очевидно, равен No.

Почему ваш вопрос дошел до вышеуказанного вопроса?

  • Динамическое использование типа компилирует в использовании типа объекта с отражением.
  • Ваш код получает объект в действительности, который (в действительности) содержит значение от одного конкретного статического типа.

Таким образом, ваш код имеет дело со статически типизированным значением, назначенным объекту, и обрабатывается как динамическое во время компиляции. Поэтому единственный случай, когда вы можете преобразовать одно статически типизированное значение в другое [без отражения], - это когда они являются частью одной и той же цепи наследования. В противном случае вы обязательно будете использовать отражение явно (написанное вами) или неявно (написанное MS с использованием Dynamic).

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

dynamic dPerson = GetDynamicPerson(); 
Person thePerson = (Person)dPerson; 

Это довольно много его.

Удовлетворительно отметить, что вы можете скопировать значения байта байтом с помощью небезопасного кода, обращающегося к адресам памяти (например, на C++), но для меня это не лучше, чем отражение.

+1

Но гораздо более предприимчивым, чем рефлексия :-) – ProfK

+0

Спасибо за разъяснение, как это работает под капотом - хорошее объяснение! – infl3x

3

Здесь мы можем преобразовать анонимный объект в словаре

Dictionary<string, object> dict = 
    obj.GetType() 
     .GetProperties() 
     .ToDictionary(p => p.Name, p => p.GetValue(obj, null)); 

Также вы можете бросить объект, используя LINQ:

List<MyType> items = anonymousType.Select(t => new MyType(t.Some, t.Other)).ToList(); 
+1

Это самое быстрое решение здесь. –