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

131

Мне нужно получить словарь свойств и их значений из объекта, объявленного с помощью ключевого слова 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 = ???
Flatliner DOA
источник

Ответы:

32

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

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

jbtule
источник
17
Благодаря тому, что мы подошли к делу, код выглядит так: var tTarget = target as IDynamicMetaObjectProvider; если (tTarget! = null) {tList.AddRange (tTarget.GetMetaObject (Expression.Constant (tTarget)). GetDynamicMemberNames ()); }
Flatliner DOA
спасибо за вашу прекрасную библиотеку. У меня возникли проблемы с переносом динамического объекта в эту библиотеку dynamicjson.codeplex.com , и я смог расширить его с помощью вашей библиотеки.
JJS
1
пример пожалуйста.
Demodave
105

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

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

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

itowlson
источник
Спасибо за это, к сожалению, образец получился несколько упрощенным. Мне нужно иметь возможность проверять динамический объект, не зная, что это за настоящий тип.
Flatliner DOA
2
Это работает только для объектов класса ExpandoObject, класс DynamicObject - еще один расширяемый класс, который не реализует IDictionary, а скорее реализует IDynamicMetaObjectProvider.
Шарике Абдулла
1
Тем не менее, он отвечает на вопрос OP.
Шарике Абдулла
58

Следует рассмотреть несколько сценариев. Прежде всего, вам необходимо проверить тип вашего объекта. Для этого вы можете просто вызвать 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, и игнорировать (или генерировать исключение) для остальных.

Александра Русина
источник
7
Мне просто кажется, что если тип, который я использую, является динамическим, то мне не следует делать предположений о его базовом типе. Я должен иметь возможность вызвать GetDynamicMemberNames, чтобы получить список участников. Кажется глупым, что статические типы имеют лучшую поддержку проверки во время выполнения, чем динамические!
Flatliner DOA
2
Вы можете переопределить метод GetDynamicMemberNames () для динамического объекта, чтобы он возвращал имена списков динамических членов. Проблема в том, что не гарантируется, что каждый динамический объект имеет этот метод (ExpandoObject не имеет). Неудивительно, что отражение лучше работает для статических типов. Он был создан в первую очередь для них. Что касается динамики, вам нужно больше полагаться на модульное тестирование и TDD.
Александра Русина
1
В документации MSDN для GetDynamicMemberNames () упоминается: «Этот метод существует только для целей отладки.»,
Что
19

Требуется 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 вот так:

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

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

Мистер Б
источник
Вам не нужен список для возврата.
aloisdg переходит на codidact.com
4
Отлично! Пришлось изменить атрибуты JObjectAsJObject = dynamicToGetPropertiesFor; в атрибуты объекта AsJObject = (JObject) JToken.FromObject (obj); хотя!!
k25
Просто чтобы повторить то, что сказал @ k25. Вы должны заменить JObject attributesAsJObject = dynamicToGetPropertiesFor;что - то вроде: var jObject = (JObject) JToken.FromObject(dynamicToGetPropertiesFor);. На этом этапе вы можете получить словарь имен и значений свойств, выполнив что-то вроде var objProperties = jObject.ToObject<Dictionary<string, object>>();. Имея это в руках, вы отправляетесь в гонки. Это не требует динамики. Он отлично работает со всем, что является подклассомDynamicObject
Flydog57