Как я могу отражать элементы динамического объекта?

Мне нужно получить словарь свойств и их значений из объекта, объявленного с помощью ключевого слова dynamic в .NET 4? Кажется, использование рефлексии для этого не будет работать.

Пример:

dynamic s = new ExpandoObject();
s.Path = "/Home";
s.Name = "Home";

// How do I enumerate the Path and Name properties and get their values?
IDictionary<string, object> propertyValues = ???

Ответ 1

Если IDynamicMetaObjectProvider может предоставить имена динамических членов, вы можете их получить. См. GetMemberNames в библиотеке PCL с лицензией apache Dynamitey (которую можно найти в nuget), это работает для ExpandoObject и DynamicObject, которые реализуют GetDynamicMemberNames и любые другие IDynamicMetaObjectProvider, которые предоставляют метаобъект с реализацией GetDynamicMemberNames без специального тестирования за пределами is IDynamicMetaObjectProvider.

После получения имен членов это немного больше, чтобы получить правильное значение, но Impromptu делает это, но сложнее указать только на интересные биты и иметь смысл. Здесь документация и она равна или быстрее, чем отражение, однако вряд ли будет быстрее, чем поиск словаря для expando, но он работает для любого объекта, expando, dynamic или оригинал - вы называете его.

Ответ 2

В случае ExpandoObject класс ExpandoObject фактически реализует IDictionary<string, object> для своих свойств, поэтому решение так же тривиально, как и кастинг:

IDictionary<string, object> propertyValues = (IDictionary<string, object>)s;

Обратите внимание, что это не будет работать для общих динамических объектов. В этих случаях вам нужно будет спуститься к DLR через IDynamicMetaObjectProvider.

Ответ 3

Существует несколько сценариев. Прежде всего, вам нужно проверить тип вашего объекта. Вы можете просто вызвать GetType() для этого. Если тип не реализует IDynamicMetaObjectProvider, вы можете использовать отражение так же, как и для любого другого объекта. Что-то вроде:

var propertyInfo = test.GetType().GetProperties();

Однако для реализации IDynamicMetaObjectProvider простое отражение не работает. В принципе, вам нужно больше узнать об этом объекте. Если это ExpandoObject (который является одной из реализаций IDynamicMetaObjectProvider), вы можете использовать ответ, предоставленный itowlson. ExpandoObject сохраняет свои свойства в словаре, и вы можете просто превратить свой динамический объект в словарь.

Если это DynamicObject (другая реализация IDynamicMetaObjectProvider), тогда вам нужно использовать любые методы, которые предоставляет этот DynamicObject. DynamicObject не требуется фактически "хранить" свой список свойств в любом месте. Например, он может сделать что-то вроде этого (я повторно использую пример из моего сообщения в блоге):

public class SampleObject : DynamicObject
{
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        result = binder.Name;
        return true;
    }
}

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

dynamic obj = new SampleObject();
Console.WriteLine(obj.SampleProperty);
//Prints "SampleProperty".

Итак, вам не о чем подумать - этот объект не имеет никаких свойств, и в то же время будут работать все допустимые имена свойств.

Я бы сказал, что для реализаций IDynamicMetaObjectProvider вам нужно фильтровать известные реализации, где вы можете получить список свойств, таких как ExpandoObject, и игнорировать (или исключать исключение) для остальных.

Ответ 4

Требуется Newtonsoft Json.Net

Немного поздно, но я придумал это. Он дает вам только ключи, а затем вы можете использовать их в динамике:

public List<string> GetPropertyKeysForDynamic(dynamic dynamicToGetPropertiesFor)
{
    JObject attributesAsJObject = dynamicToGetPropertiesFor;
    Dictionary<string, object> values = attributesAsJObject.ToObject<Dictionary<string, object>>();
    List<string> toReturn = new List<string>();
    foreach (string key in values.Keys)
    {
        toReturn.Add(key);                
    }
    return toReturn;
}

Затем вы просто заходите так:

foreach(string propertyName in GetPropertyKeysForDynamic(dynamicToGetPropertiesFor))
{
    dynamic/object/string propertyValue = dynamicToGetPropertiesFor[propertyName];
    // And
    dynamicToGetPropertiesFor[propertyName] = "Your Value"; // Or an object value
}

Выбор значения в виде строки или какого-либо другого объекта или другого динамика и повторного поиска.