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


180

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

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

如果我仅将“ value2”作为字符串,如何获取d.value2的值(“随机”)?在javascript中,我可以执行d [“ value2”]来访问值(“随机”),但是我不确定如何使用c#和反射来做到这一点。我最接近的是:

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

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


26
请注意,这不是“动态”的预期目的,并且此方案在“动态”下的效果不如在“对象”下更好。当在编译时知道属性名称但不知道类型名称时,“动态”功能可以访问属性。由于您在编译时既不知道名称也不知道类型,因此dynamic不会对您有所帮助。
埃里克·利珀特


3
@EricLippert我知道这个问题很旧,但是只是发表评论,以防将来有人看到它。在某些情况下,您无法选择使用动态还是对象(例如,当使用JSON解析器时),并且您可能仍想从字符串(例如,从配置文件中)获取属性,因此这种用法并不常见最初可能会想到。
Pedrom'1

Answers:


216

获得PropertyInfo(from 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 2011年

6
认为GetValue需要一个附加参数-egdGetType()。GetProperty(“ value2”)。GetValue(d,null)
dommer 2011年

3
这将对真正的动态ExpandoObject而不是匿名类型起作用吗?由于new {}使用定义的属性创建了一个真正的匿名类型,因此调用GetType / GetProperty是有意义的,但是ExpandoObject又如何呢?如果调用GetType,则将获得具有ExpandoObject属性的类型,但不一定具有其动态属性。
Triynko 2014年

16
-1。这仅适用于转换为动态的简单.NET对象。它不会有任何自定义动态对象的工作就像为Expando或ViewBag使用ASP.NET MVC
菲利普·穆宁

8
这就是Expando Object的工作原理:((((IDictionary <string,object>)x))[“ value1”]
Michael Bahig 2015年

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,即使在IronPython中的Python对象上也可以使用)。
Trevor Sundberg 2014年

目标对象是什么?
Demodave

@Demodave要在其上调用属性的对象(d在问题中)。
IllidanS4希望Monica退回2015年

➕1当FastMember和HyperDescriptor都不能使用时,这对私有财产起作用
Chris Marisic '16

@ IllidanS4在比较CallSite代码与CallByName代码时,是否在缓存CallSite实例时比较了两者?我怀疑您第一种方法的成本几乎纯粹是Binder和的激活,而CallSite不是调用Target()
Chris Marisic

24

Dynamitey是一个开放源代码.net std库,让您像使用dynamic关键字一样调用它,但是使用字符串作为属性名,而不是使用编译器为您代劳,它最终在速度上等于反射(速度不快)就像使用dynamic关键字一样,但这是由于动态缓存(编译器静态缓存的地方)的额外开销所致。

Dynamic.InvokeGet(d,"value2");

11

获得这两个一个最简单的方法settergetter用于其适用于任何类型,包括属性dynamicExpandoObject方法是使用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?。
Triynko 2014年

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:无法将[]的索引应用于类型'object'的表达式。用于new JavaScriptSerializer().Deserialize<object>(json);以您建议的方式访问“属性”
Kris Kilton

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

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

d?.value2 , d?.value3

0

与接受的答案类似,您也可以尝试GetField代替GetProperty

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

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


在C#3.0+中,属性与字段之间的FYI差异:stackoverflow.com/a/653799/2680660
Efreeto
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.