Получить значение динамического свойства C# через строку
Я хотел бы получить доступ к значению dynamic
Свойство C# со строкой:
dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };
Как я могу получить значение d.value2 ("random"), если у меня есть только "value2" как строка? В javascript я мог бы сделать d ["value2"] для доступа к значению ("random"), но я не уверен, как это сделать с помощью C# и отражения. Самое близкое, что я пришел, это:
d.GetType().GetProperty("value2")
... но я не знаю, как получить реальную стоимость от этого.
Как всегда, спасибо за вашу помощь!
15 ответов
Как только у вас есть PropertyInfo
(от GetProperty
), вам нужно позвонить GetValue
и передать экземпляр, из которого вы хотите получить значение. В твоем случае:
d.GetType().GetProperty("value2").GetValue(d, null);
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);
}
Динамит - это открытый исходный код .net std
библиотека, что давайте назовем это как dynamic
ключевое слово, но с использованием строки для имени свойства, а не компилятора, который делает это за вас, и в конечном итоге он становится равным отражению по скорости (что не так быстро, как при использовании ключевого слова dynamic, но это связано с дополнительными издержками кэширование динамически, где компилятор статически кэшируется).
Dynamic.InvokeGet(d,"value2");
Самый простой способ получения как setter
и 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");
В большинстве случаев, когда вы запрашиваете динамический объект, вы получаете ExpandoObject (не в приведенном выше примере с анонимным, но статически типизированным вопросом, но вы упоминаете JavaScript, и мой выбранный JSON-анализатор JsonFx, например, генерирует ExpandoObjects).
Если ваша динамика на самом деле является ExpandoObject, вы можете избежать отражения, приведя ее к IDictionary, как описано на http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx.
Как только вы приведете к IDictionary, у вас есть доступ к полезным методам, таким как.Item и.ContainsKey.
GetProperty/GetValue не работает для данных Json, он всегда генерирует нулевое исключение, однако вы можете попробовать этот подход:
Сериализуйте ваш объект с помощью JsonConvert:
var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));
Затем получите к нему доступ, приведя его обратно к строке:
var pn = (string)z["DynamicFieldName"];
Это может работать напрямую, применяя Convert.ToString(request)["DynamicFieldName"], однако я не проверял.
Чтобы получить свойства из динамического документа, когда .GetType()
возвращается null
, попробуй это:
var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;
d.GetType().GetProperty("значение2")
возвращает объект PropertyInfo.
Так тогда делай
propertyInfo.GetValue(d)
Вот как я получил значение свойства динамического объекта:
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)
{
}
}
Некоторые из решений не работали с объектом типа значения, который я получил из строки json, возможно, потому, что в моем коде не было конкретного типа, похожего на объект, который я получил бы из строки json, так как я пошел об этом было
JsonElement myObject = System.Text.Json.JsonSerializer.Deserialize<JsonElement>(jsonStringRepresentationOfMyObject);
/*In this case I know that there is a property with
the name Code, otherwise use TryGetProperty. This will
still return a JsonElement*/
JsonElement propertyCode = myObject.GetProperty("Code");
/*Now with the JsonElement that represents the property,
you can use several methods to retrieve the actual value,
in this case I know that the value in the property is a string,
so I use the GetString method on the object. If I knew the value
was a double, then I would use the GetDouble() method on the object*/
string code = propertyCode.GetString();
Это сработало для меня
В.Net core 3.1 вы можете попробовать вот так
d?.value2 , d?.value3
Как и в принятом ответе, вы также можете попробовать GetField
вместо того GetProperty
.
d.GetType().GetField("value2").GetValue(d);
В зависимости от того, насколько реально Type
был реализован, это может работать, когда GetProperty() не работает, и может быть даже быстрее.
Если у вас есть динамическая переменная, например, DapperRow, вы можете сначала создать ExpandoObject, а затем преобразовать Expando в IDictionary<string, object>. С этого момента возможно получение значения через имя свойства.
Вспомогательный метод ToExpandoObject:
public static ExpandoObject ToExpandoObject(object value)
{
IDictionary<string, object> dapperRowProperties = value as IDictionary<string, object>;
IDictionary<string, object> expando = new ExpandoObject();
if (dapperRowProperties == null)
{
return expando as ExpandoObject;
}
foreach (KeyValuePair<string, object> property in dapperRowProperties)
{
if (!expando.ContainsKey(property.Key))
{
expando.Add(property.Key, property.Value);
}
else
{
//prefix the colliding key with a random guid suffixed
expando.Add(property.Key + Guid.NewGuid().ToString("N"), property.Value);
}
}
return expando as ExpandoObject;
}
Пример использования, я выделил жирным шрифтом приведение, которое дает нам доступ в примере, я отметил важные биты буквами **:
using (var transactionScope = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
foreach (var dynamicParametersForItem in dynamicParametersForItems)
{
var idsAfterInsertion = (await connection.QueryAsync<object>(sql, dynamicParametersForItem)).ToList();
if (idsAfterInsertion != null && idsAfterInsertion.Any())
{
**var idAfterInsertionDict = (IDictionary<string, object>) ToExpandoObject(idsAfterInsertion.First());**
string firstColumnKey = columnKeys.Select(c => c.Key).First();
**object idAfterInsertionValue = idAfterInsertionDict[firstColumnKey];**
addedIds.Add(idAfterInsertionValue); //we do not support compound keys, only items with one key column. Perhaps later versions will return multiple ids per inserted row for compound keys, this must be tested.
}
}
}
В моем примере я ищу значение свойства внутри динамического объекта DapperRow и сначала преобразую строку Dapper в ExpandoObject и помещаю ее в пакет свойств словаря, как показано и упомянуто в других ответах здесь.
Мой пример кода - это метод InsertMany для расширения Dapper, над которым я работаю, я хотел захватить несколько идентификаторов здесь после пакетной вставки.
Внутри хранимой процедуры:
INSERT INTO [dbo].[Account] ([Title]....)
OUTPUT INSERTED.Id
VALUES( @Title.....)
Код С#:
var id = await _dbConnection.ExecuteScalarAsync("CreateAccount", param,
null, null, CommandType.StoredProcedure);
account.Id = (int)id;
Вот окно отладки со значением, мы получаем его как объект, поэтому преобразуем его в int.
Использоватьdynamic
сNewtonsoft.Json.JsonConvert.DeserializeObject
:
// Get JSON string of object
var obj = new { value1 = "some", value2 = "random", value3 = "value" };
var jsonString = JsonConvert.SerializeObject(obj);
// Use dynamic with JsonConvert.DeserializeObject
dynamic d = JsonConvert.DeserializeObject(jsonString);
// output = "some"
Console.WriteLine(d["value1"]);
Образец: https://dotnetfiddle.net/XGBLU1