通过字符串获取c#动态属性的值

Posted

技术标签:

【中文标题】通过字符串获取c#动态属性的值【英文标题】:Get value of c# dynamic property via string 【发布时间】:2011-06-23 19:04:59 【问题描述】:

我想用字符串访问dynamic c# 属性的值:

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

如果我只有 "value2" 作为字符串,我如何获得 d.value2 ("random") 的值?在 javascript 中,我可以执行 d["value2"] 来访问值("random"),但我不确定如何使用 c# 和反射来执行此操作。我最接近的是:

d.GetType().GetProperty("value2") ...但我不知道如何从中获取实际值。

一如既往,感谢您的帮助!

【问题讨论】:

请注意,这不是“动态”的预期目的,并且这种情况与“动态”相比并不比“对象”更好。当属性的 name 在编译时已知但 type 未知时,“动态”可以访问属性。由于您在编译时既不知道名称也不知道类型,因此 dynamic 不会帮助您。 可能相关:***.com/questions/5877251/…. @EricLippert 我知道这个问题很老,但只是为了发表评论,以防将来有人看到它。在某些情况下,您无法选择是使用动态还是对象(例如在使用 JSON 解析器时),并且您仍然可能希望从字符串(例如从配置文件)中获取属性,因此这种使用并不罕见正如人们最初所想的那样。 【参考方案1】:

获得PropertyInfo(来自GetProperty)后,您需要调用GetValue 并传入要从中获取值的实例。在你的情况下:

d.GetType().GetProperty("value2").GetValue(d, null);

【讨论】:

我在监视窗口中收到了'd.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic System.Reflection.TargetInvocationException...? 认为 GetValue 需要一个额外的参数 - 例如d.GetType().GetProperty("value2").GetValue(d, null) 这是否适用于真正的动态 ExpandoObject 而不是匿名类型?由于new 创建了一个具有定义属性的真正匿名类型,调用GetType/GetProperty 是有意义的,但是ExpandoObject 呢,如果你调用GetType,你会得到一个具有ExpandoObject 属性的类型,但不一定是它的动态属性. -1。这仅适用于转换为动态的简单 .NET 对象。它不适用于任何自定义动态对象,如 Expando 或 ViewBag 使用的 ASP.NET MVC 这适用于 Expando 对象:(((IDictionary)x))["value1"]【参考方案2】:

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

返回一个 PropertyInfo 对象。

那就做吧

propertyInfo.GetValue(d)

【讨论】:

谢谢,这是正确答案,但如上所述,GetValue(d) 必须是 GetValue(d,null)【参考方案3】:

Dynamitey 是一个开源的.net std 库,你可以像dynamic 关键字一样调用它,但是使用字符串作为属性名称而不是编译器为你做的,它最终是equal to reflection speedwise(这几乎没有使用 dynamic 关键字快,但这是由于动态缓存的额外开销,编译器静态缓存)。

Dynamic.InvokeGet(d,"value2");

【讨论】:

【参考方案4】:
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);

【讨论】:

只是想提一下,VisualBasic 版本并不等同于您原来的“GetProperty”版本(GetProperty 实际上调用动态 GetMember,它甚至适用于 IronPython 中的 Python 对象)。 ➕1 当 FastMember 和 HyperDescriptor 都不起作用时,这适用于私有属性 @IllidanS4 当您比较CallSite 代码与CallByName 代码时,您是否在缓存CallSite 实例时比较了两者?我怀疑您的第一种方法的成本几乎纯粹是激活BinderCallSite,而不是调用Target() 我想知道您是否可以在尝试获取其值之前检查该属性是否存在? -- 到目前为止,这是唯一接近我需要的东西(因为我正在使用 COM 对象,它们本身是动态的) @Daniel 对于某些对象,您可以调用IDispatch::GetTypeInfoITypeInfo::GetFuncDesc。虽然它可能不适用于某些类型。【参考方案5】:

很多时候,当您请求动态对象时,您会得到一个 ExpandoObject(不是在上面问题的匿名但静态类型的示例中,但您提到 JavaScript 和我选择的 JSON 解析器 JsonFx,例如,生成 ExpandoObjects )。

如果您的动态实际上是一个 ExpandoObject,您可以通过将其转换为 IDictionary 来避免反射,如 http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx 所述。

转换为 IDictionary 后,您可以访问有用的方法,例如 .Item 和 .ContainsKey

【讨论】:

不幸的是,例如,必须强制转换为 IDictionary 并使用 TryGetValue 会导致返回一个普通的旧对象。此时您不能利用隐式运算符,因为它们仅在编译时考虑。例如,如果我有一个隐式转换为 Int64? 的 Int64Proxy 类,那么 Int64? i = data.value; //data is ExpandoObject 将自动查找并调用隐式运算符。另一方面,如果我必须使用 IDictionary 来测试“值”字段是否存在,我会得到一个不会无误地转换为 Int64 的对象?。【参考方案6】:

这是我获得动态属性值的方法:

    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)
        

        


    

【讨论】:

【参考方案7】:

为适用于任何类型(包括dynamicExpandoObject)的属性同时获取settergetter 的最简单方法是使用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】:

GetProperty/GetValue 不适用于 Json 数据,它总是会产生一个空异常,但是,您可以尝试这种方法:

使用 JsonConvert 序列化您的对象:

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

然后访问它直接将其转换回字符串:

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

它可以直接应用 Convert.ToString(request)["DynamicFieldName"],但是我还没有测试过。

【讨论】:

此方法生成错误:错误 CS0021:无法将 [] 索引应用于“对象”类型的表达式。使用new JavaScriptSerializer().Deserialize&lt;object&gt;(json); 以您建议的方式访问“属性” 这就像一个魅力! 2020 年 11 月 20 日,谢谢老兄【参考方案9】:

从动态文档中获取属性 当.GetType() 返回null 时,试试这个:

var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;

【讨论】:

【参考方案10】:

在 .Net core 3.1 中你可以这样尝试

d?.value2 , d?.value3

【讨论】:

【参考方案11】:

与接受的答案类似,您也可以尝试GetField 而不是GetProperty

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

根据实际 Type 的实现方式,当 GetProperty() 不可用时,这可能会起作用,甚至可以更快。

【讨论】:

仅供参考 C# 3.0+ 中属性和字段之间的区别:***.com/a/653799/2680660【参考方案12】:

如果您有一个动态变量,例如 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。

【讨论】:

【参考方案13】:

一些解决方案不适用于我从 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();

这对我有用

【讨论】:

对我最好,谢谢

以上是关于通过字符串获取c#动态属性的值的主要内容,如果未能解决你的问题,请参考以下文章

字符串长度超过maxJsonLength属性设置的值。c#

如何通过反射获取字符串属性的值?

如何从动态对象中获取反序列化的 xml 属性

c#如何获取comboBox当前选中的值

C# 属性控件(propertyGrid),如何动态添加下拉框中的值。例如:Name : 下拉框中的值:小米,小明。

Python如何通过字符或数字动态获取对象的名称或者属性?