我想使用字符串访问 dynamic
c# 属性的值:
dynamic d = new { value1 = "some", value2 = "random", value3 = "value" };
如果我只有“value2”作为字符串,如何获得 d.value2 (“random”) 的值?在 javascript 中,我可以执行 d["value2"] 来访问值("random"),但我不确定如何使用 c# 和反射来执行此操作。我最接近的是:
d.GetType().GetProperty("value2")
...但我不知道如何从中获得实际价值。
一如既往,感谢您的帮助!
获得 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 的引用。也适用于动态类型和私有属性和字段。
编辑:虽然这种方法有效,但 Microsoft.VisualBasic.dll 程序集的速度几乎快了 20 倍:
public static object GetProperty(object target, string name)
{
return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}
CallSite
代码与 CallByName
代码时,您是否在缓存 CallSite
实例时比较了两者?我怀疑您的第一种方法的成本几乎纯粹是激活 Binder
和 CallSite
,而不是调用 Target()
IDispatch::GetTypeInfo
和 ITypeInfo::GetFuncDesc
。虽然它可能不适用于某些类型。
Dynamitey 是一个开源 .net std
库,您可以将其称为 dynamic
关键字,但使用字符串作为属性名称,而不是编译器为您完成,它最终成为 equal to reflection speedwise (这几乎没有使用 dynamic 关键字那么快,但这是由于动态缓存的额外开销,编译器静态缓存)。
Dynamic.InvokeGet(d,"value2");
获取适用于任何类型(包括 dynamic
和 ExpandoObject
)的属性的 setter
和 getter
的最简单方法是使用 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
Int64? i = data.value; //data is ExpandoObject
将自动查找并调用隐式运算符。另一方面,如果我必须使用 IDictionary 来测试“值”字段是否存在,我会得到一个不会无误地转换为 Int64 的对象?
GetProperty/GetValue 不适用于 Json 数据,它总是会生成 null 异常,但是,您可以尝试以下方法:
使用 JsonConvert 序列化您的对象:
var z = Newtonsoft.Json.JsonConvert.DeserializeObject(Convert.ToString(request));
然后访问它直接将其转换回字符串:
var pn = (string)z["DynamicFieldName"];
它可以直接应用 Convert.ToString(request)["DynamicFieldName"],但是我没有测试过。
new JavaScriptSerializer().Deserialize<object>(json);
以您建议的方式访问“属性”
d.GetType().GetProperty("value2")
返回一个 PropertyInfo 对象。
那么做
propertyInfo.GetValue(d)
GetValue(d)
必须是 GetValue(d,null)
这是我获得动态属性值的方式:
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)
{
}
}
要在 .GetType()
返回 null
时从动态文档中获取属性,请尝试以下操作:
var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;
在.Net core 3.1中你可以这样尝试
d?.value2 , d?.value3
一些解决方案不适用于我从 json 字符串获得的 valuekind 对象,可能是因为我的代码中没有与我从 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();
这对我有用
与接受的答案类似,您也可以尝试使用 GetField
而不是 GetProperty
。
d.GetType().GetField("value2").GetValue(d);
根据实际 Type
的实现方式,当 GetProperty() 不可用时,这可能会起作用,甚至可以更快。
如果您有一个动态变量,例如 DapperRow,您可以先构建一个 ExpandoObject,然后将 Expando 转换为 IDictionary
辅助方法 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 并将其转换为字典属性包,如此处的其他答案所示和提到的。
我的示例代码是我正在处理的 Dapper 扩展的 InsertMany 方法,我想在批量插入之后在这里抓住多个 id。
不定期副业成功案例分享
'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}
......?new {}
创建了一个具有已定义属性的真正匿名类型,因此调用 GetType/GetProperty 是有意义的,但是 ExpandoObject 呢,如果您调用 GetType,您将获得一个具有 ExpandoObject 属性但不一定具有其动态属性的类型.