Отримайте значення c # динамічного властивості за допомогою рядка


182

Я б хотів отримати доступ до значення властивості dynamicc # рядком:

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

Як я можу отримати значення d.value2 ("випадковий"), якщо у мене є лише "value2" як рядок? У JavaScript я можу зробити d ["value2"] для доступу до значення ("випадковий"), але я не впевнений, як це зробити за допомогою c # та відображення. Найближче до мене це:

d.GetType().GetProperty("value2") ... але я не знаю, як отримати від цього фактичну цінність.

Як завжди, дякую за вашу допомогу!


26
Зауважте, що це не є цільовим призначенням "динамічного" і що цей сценарій не працює краще з "динамічним", ніж з "об'єктом". "динамичний" дозволяє отримати доступ до властивостей, коли ім'я властивості відомо під час компіляції, але тип - ні. Оскільки ви не знаєте ні імені, ні типу під час компіляції, динаміка вам не допоможе.
Ерік Ліпперт

Можливо , пов'язані з : 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
Philipp Munin

8
це те, що працює з об’єктом Expando: (((IDictionary <string, object>) x)) ["value1"]
Michael Bahig

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. Працює також для динамічних типів та приватних властивостей та полів.

Редагувати : Хоча цей підхід працює, зі складання Microsoft.VisualBasic.dll існує майже на 20 × швидший метод :

public static object GetProperty(object target, string name)
{
    return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}

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.InvokeGet(d,"value2");

11

Найпростіший метод отримання властивостей 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, щоб перевірити, чи існує поле "value", я б повернув об'єкт, який не буде передаватися без помилок Int64 ?.
Трайнко

5

GetProperty / GetValue не працює для даних Json, він завжди генерує нульовий виняток, проте ви можете спробувати такий підхід:

Серіалізуйте об'єкт за допомогою JsonConvert:

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

Потім перейдіть до нього безпосередньо, повертаючи його до рядка:

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

Це може працювати прямо, застосовуючи Convert.ToString (запит) ["DynamicFieldName"], однак я не перевіряв.


2
Цей метод генерує помилку: помилка CS0021: Неможливо застосувати індексацію за допомогою [] до виразу типу "об'єкт". Використовуйте, new JavaScriptSerializer().Deserialize<object>(json);щоб дістатися до "властивостей" так, як ви запропонували
Кріс Кілтон

4

d.GetType (). GetProperty ("value2")

повертає об'єкт 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

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

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

Залежно від того, як реально Typeбуло реалізовано, це може працювати, коли GetProperty () не працює і навіть може бути швидшим.


FYI різниця між власності і поля в C # 3.0 +: stackoverflow.com/a/653799/2680660
Efreeto
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.