Получить значение динамического свойства c # через строку

182

Я хотел бы получить доступ к значению dynamicсвойства c # с помощью строки:

dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };

Как я могу получить значение d.value2 ("random"), если у меня есть только "value2" как строка? В javascript я мог бы сделать d ["value2"] для доступа к значению ("random"), но я не уверен, как это сделать с помощью c # и отражения. Самое близкое, что я пришел, это:

d.GetType().GetProperty("value2") ... но я не знаю, как получить реальную стоимость от этого.

Как всегда, спасибо за вашу помощь!

TimDog
источник
26
Обратите внимание, что это не предназначенная цель «динамического» и что этот сценарий не работает лучше с «динамическим», чем с «объектом». «dynamic» позволяет получить доступ к свойствам, когда имя свойства известно во время компиляции, а тип - нет. Так как вы не знаете ни имени, ни типа во время компиляции, динамический вам не поможет.
Эрик Липперт
Возможно связано: stackoverflow.com/questions/5877251/… .
DuckMaestro
3
@EricLippert Я знаю, что этот вопрос старый, но я просто хочу прокомментировать, если кто-то увидит его в будущем. В некоторых случаях вы не можете выбрать, использовать ли динамический объект или объект (например, при использовании анализатора JSON), и вам все еще может потребоваться получить свойства из строки (например, из файла конфигурации), поэтому такое использование не является чем-то необычным как можно подумать
Педром

Ответы:

217

Как только вы получите свой PropertyInfo(от GetProperty), вам нужно вызвать GetValueи передать экземпляр, из которого вы хотите получить значение. В твоем случае:

d.GetType().GetProperty("value2").GetValue(d, null);
Адам Робинсон
источник
4
Я получаю 'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}в окне с этим ..?
TimDog
6
Думаю, что GetValue нужен дополнительный параметр - egdGetType (). GetProperty ("value2"). GetValue (d, null)
dommer
3
Будет ли это работать с настоящим динамическим ExpandoObject, а не с анонимным типом? Поскольку new {}создается реальный анонимный тип с определенными свойствами, вызов GetType / GetProperty имеет смысл, но как насчет ExpandoObject, который, если вы вызовете GetType, вы получите тип, который имеет свойства ExpandoObject, но не обязательно его динамические свойства.
Триынко
16
-1. Это работает только с простыми объектами .NET, которые были преобразованы в динамические. Он не будет работать с любым настраиваемым динамическим объектом, таким как Expando или ViewBag, используемым ASP.NET MVC
Филипп Мунин,
8
это то, что работает с Expando Object: (((IDictionary <string, object>) x)) ["value1"]
Майкл Бахиг
39
public static object GetProperty(object target, string name)
{
    var site = System.Runtime.CompilerServices.CallSite<Func<System.Runtime.CompilerServices.CallSite, object, object>>.Create(Microsoft.CSharp.RuntimeBinder.Binder.GetMember(0, name, target.GetType(), new[]{Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(0,null)}));
    return site.Target(site, target);
}

Добавить ссылку на Microsoft.CSharp. Работает также для динамических типов и частных свойств и полей.

Изменить : хотя этот подход работает, есть почти в 20 раз более быстрый метод из сборки Microsoft.VisualBasic.dll :

public static object GetProperty(object target, string name)
{
    return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}
IllidanS4 хочет вернуть Монику
источник
2
Просто хотел отметить, что версия VisualBasic не эквивалентна вашей исходной версии GetProperty (GetProperty фактически вызывает динамический GetMember, который работает даже на объектах Python в IronPython).
Тревор Сундберг
какой будет цель объекта?
Демодава
@Demodave Объект, для которого вы хотите вызвать свойство ( dв вопросе).
IllidanS4 хочет вернуть Монику
This1 это работало для частных объектов, когда не было бы FastMember и HyperDescriptor
Крис Марисик,
@ IllidanS4, когда вы сравнивали CallSiteкод с CallByNameкодом, сравнивали ли вы их при кэшировании CallSiteэкземпляра? Я подозреваю, что стоимость вашего первого метода - это почти чисто активация, Binderа CallSiteне вызовTarget()
Крис Марисик
24

Dynamitey - это .net stdбиблиотека с открытым исходным кодом , которая позволяет вам называть ее как dynamicключевое слово, но используя строку для имени свойства, а не компилятор, который делает это за вас, и в итоге она оказывается равной отражению по скорости (что не так быстро с использованием ключевого слова dynamic, но это связано с дополнительными издержками динамического кэширования, когда компилятор статически кэшируется).

Dynamic.InvokeGet(d,"value2");
jbtule
источник
11

Самый простой метод для получения как a, так setterи a getterдля свойства, которое работает для любого типа, в том числе, dynamicи ExpandoObjectзаключается в использовании, FastMemberкоторый также является самым быстрым методом (он использует Emit).

Вы можете получить TypeAccessorоснованный на заданном типе или ObjectAccessorоснованный на экземпляре заданного типа.

Пример:

var staticData = new Test { Id = 1, Name = "France" };
var objAccessor = ObjectAccessor.Create(staticData);
objAccessor["Id"].Should().Be(1);
objAccessor["Name"].Should().Be("France");

var anonymous = new { Id = 2, Name = "Hilton" };
objAccessor = ObjectAccessor.Create(anonymous);
objAccessor["Id"].Should().Be(2);
objAccessor["Name"].Should().Be("Hilton");

dynamic expando = new ExpandoObject();
expando.Id = 3;
expando.Name = "Monica";
objAccessor = ObjectAccessor.Create(expando);
objAccessor["Id"].Should().Be(3);
objAccessor["Name"].Should().Be("Monica");

var typeAccessor = TypeAccessor.Create(staticData.GetType());
typeAccessor[staticData, "Id"].Should().Be(1);
typeAccessor[staticData, "Name"].Should().Be("France");

typeAccessor = TypeAccessor.Create(anonymous.GetType());
typeAccessor[anonymous, "Id"].Should().Be(2);
typeAccessor[anonymous, "Name"].Should().Be("Hilton");

typeAccessor = TypeAccessor.Create(expando.GetType());
((int)typeAccessor[expando, "Id"]).Should().Be(3);
((string)typeAccessor[expando, "Name"]).Should().Be("Monica");
Майя
источник
8

Большую часть времени, когда вы запрашиваете динамический объект, вы получаете ExpandoObject (не в приведенном выше примере с анонимным, но статически типизированным вопросом, но вы упоминаете JavaScript, и мой выбранный JSON-анализатор JsonFx, например, генерирует ExpandoObjects).

Если ваша динамика на самом деле является ExpandoObject, вы можете избежать отражения, приведя его к IDictionary, как описано по адресу http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx .

Как только вы приведете к IDictionary, у вас есть доступ к полезным методам, таким как .Item и .ContainsKey.

Фрэнсис Нортон
источник
К сожалению, необходимость приведения к IDictionary и использования, например, TryGetValue приводит к возвращению простого старого объекта. Вы не можете воспользоваться неявными операторами в этой точке, так как они рассматриваются только во время компиляции. Например, если бы у меня был класс Int64Proxy с неявным преобразованием в Int64?, То автоматически выполнял Int64? i = data.value; //data is ExpandoObjectбы поиск и вызывал бы неявный оператор. С другой стороны, если бы мне пришлось использовать IDictionary, чтобы проверить, существует ли поле «значение», я бы получил объект обратно, который не будет без ошибок приведен к Int64 ?.
Триынко
5

GetProperty / GetValue не работает для данных Json, он всегда генерирует нулевое исключение, однако вы можете попробовать этот подход:

Сериализуйте ваш объект с помощью JsonConvert:

var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));

Затем получите к нему доступ, приведя его обратно к строке:

var pn = (string)z["DynamicFieldName"];

Это может работать напрямую, применяя Convert.ToString (request) ["DynamicFieldName"], однако я не проверял.

Андерсон
источник
2
Этот метод генерирует ошибку: ошибка CS0021: Невозможно применить индексирование с помощью [] к выражению типа «объект». Используйте, new JavaScriptSerializer().Deserialize<object>(json);чтобы добраться до «свойств» так, как вы предлагали
Крис Килтон,
4

d.GetType (). GetProperty ( "значение2")

возвращает объект PropertyInfo.

Так тогда делай

propertyInfo.GetValue(d)
Джеймс Гонт
источник
2
спасибо, это был правильный ответ, но, как уже упоминалось выше, GetValue(d)необходимо, чтобыGetValue(d,null)
TimDog
4

Вот как я получил значение свойства динамического объекта:

    public dynamic Post(dynamic value)
    {            
        try
        {
            if (value != null)
            {
                var valorCampos = "";

                foreach (Newtonsoft.Json.Linq.JProperty item in value)
                {
                    if (item.Name == "valorCampo")//property name
                        valorCampos = item.Value.ToString();
                }                                           

            }
        }
        catch (Exception ex)
        {

        }


    }
Марсело Лима Брага
источник
1

Чтобы получить свойства из динамического документа при .GetType()возврате null, попробуйте это:

var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;
Ижай Брюин
источник
0

В .Net core 3.1 вы можете попробовать вот так

d?.value2 , d?.value3
vyeluri5
источник
0

Подобно принятому ответу, вы также можете попробовать GetFieldвместо GetProperty.

d.GetType().GetField("value2").GetValue(d);

В зависимости от того, как Typeбыл реализован факт , это может работать, когда GetProperty () не работает и может даже быть быстрее.

Efreeto
источник
К вашему сведению разница между свойством и полем в C # 3.0+: stackoverflow.com/a/653799/2680660
Efreeto