ChatGPT解决这个技术问题 Extra ChatGPT

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

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

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

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

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

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

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

A
Adam Robinson

获得 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 需要一个额外的参数 - egdGetType().GetProperty("value2").GetValue(d, null)
这是否适用于真正的动态 ExpandoObject 而不是匿名类型?由于 new {} 创建了一个具有已定义属性的真正匿名类型,因此调用 GetType/GetProperty 是有意义的,但是 ExpandoObject 呢,如果您调用 GetType,您将获得一个具有 ExpandoObject 属性但不一定具有其动态属性的类型.
-1。这仅适用于转换为动态的简单 .NET 对象。它不适用于使用 ASP.NET MVC 的任何自定义动态对象,例如 Expando 或 ViewBag
这适用于 Expando 对象: (((IDictionary)x))["value1"]
I
IS4
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。虽然它可能不适用于某些类型。
j
jbtule

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

Dynamic.InvokeGet(d,"value2");

M
MaYaN

获取适用于任何类型(包括 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");

F
Francis Norton

大多数时候,当您请求动态对象时,您会得到一个 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 的对象?
A
Anderson

GetProperty/GetValue 不适用于 Json 数据,它总是会生成 null 异常,但是,您可以尝试以下方法:

使用 JsonConvert 序列化您的对象:

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

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

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

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


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

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

返回一个 PropertyInfo 对象。

那么做

propertyInfo.GetValue(d)

谢谢,这是正确的答案,但如上所述,GetValue(d) 必须是 GetValue(d,null)
M
Marcelo Lima Braga

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

    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)
        {

        }


    }

T
Trevor Reid

要在 .GetType() 返回 null 时从动态文档中获取属性,请尝试以下操作:

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

v
vyeluri5

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

d?.value2 , d?.value3

k
kudzanayi

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

这对我有用


最适合我,谢谢
E
Efreeto

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

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

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


仅供参考 C# 3.0+ 中属性和字段之间的区别:stackoverflow.com/a/653799/2680660
T
Tore Aurstad

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