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

发布于 2024-10-16 15:49:13 字数 364 浏览 1 评论 0原文

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

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

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

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

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

I'd like to access the value of a dynamic c# property with a string:

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

How can I get the value of d.value2 ("random") if I only have "value2" as a string? In javascript, I could do d["value2"] to access the value ("random"), but I'm not sure how to do this with c# and reflection. The closest I've come is this:

d.GetType().GetProperty("value2") ... but I don't know how to get the actual value from that.

As always, thanks for your help!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

凉宸 2024-10-23 15:49:14

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

对我有用

Some of the solutions were not working with a valuekind object that I obtained from a json string, maybe because I did not have a concrete type in my code that was similar to the object that I would obtain from the json string, so how I went about it was

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();

That worked for me

我不是你的备胎 2024-10-23 15:49:14

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

d?.value2 , d?.value3

In .Net core 3.1 you can try like this

d?.value2 , d?.value3
烂柯人 2024-10-23 15:49:14

dynamicNewtonsoft.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

Use dynamic with 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"]);

Sample:
https://dotnetfiddle.net/XGBLU1

淡莣 2024-10-23 15:49:14

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

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

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

Similar to the accepted answer, you can also try GetField instead of GetProperty.

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

Depending on how the actual Type was implemented, this may work when GetProperty() doesn't and can even be faster.

被你宠の有点坏 2024-10-23 15:49:14

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

In case you have a dynamic variable such as a DapperRow for example you can first build up an ExpandoObject, then cast the Expando into an IDictionary<string, object>. From then on, getting a value via the name of a property is possible.

Helper method 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;
    }

Sample usage, I have marked in bold the casting which gives us access in the example, I have marked the important bits with the ** letters:

  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.
                }
            }
        }

In my example, I look up a property value inside a dynamic object DapperRow and first convert the Dapper row into an ExpandoObject and cast it into a dictionary property bag as shown and mentioned in other answers here.

My sample code is the InsertMany method for Dapper extension I am working on, I wanted to grab hold of the multiple ids here after the batch insert.

空城旧梦 2024-10-23 15:49:14

内部存储过程:

INSERT INTO [dbo].[Account] ([Title]....)
OUTPUT INSERTED.Id
VALUES( @Title.....)

C# 代码:

var id = await _dbConnection.ExecuteScalarAsync("CreateAccount", param, 
            null, null, CommandType.StoredProcedure);
account.Id = (int)id;

这是带有值的调试窗口,我们将其作为对象获取,因此将其转换为 int。

输入图片此处描述

Inside Stored Procedure:

INSERT INTO [dbo].[Account] ([Title]....)
OUTPUT INSERTED.Id
VALUES( @Title.....)

C# Code:

var id = await _dbConnection.ExecuteScalarAsync("CreateAccount", param, 
            null, null, CommandType.StoredProcedure);
account.Id = (int)id;

Here is the debugging window with value, we are getting it as an object so convert it to int.

enter image description here

溺深海 2024-10-23 15:49:13

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

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

Once you have your PropertyInfo (from GetProperty), you need to call GetValue and pass in the instance that you want to get the value from. In your case:

d.GetType().GetProperty("value2").GetValue(d, null);
悲凉≈ 2024-10-23 15:49:13
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);
}
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);
}

Add reference to Microsoft.CSharp. Works also for dynamic types and private properties and fields.

Edit: While this approach works, there is almost 20× faster method from the Microsoft.VisualBasic.dll assembly:

public static object GetProperty(object target, string name)
{
    return Microsoft.VisualBasic.CompilerServices.Versioned.CallByName(target, name, CallType.Get);
}
清旖 2024-10-23 15:49:13

Dynamitey 是一个开源 .net std 库,您可以这样称呼它dynamic 关键字,但使用字符串作为属性名称,而不是编译器为您执行此操作,最终会变成 等于反射速度(几乎没有使用dynamic关键字那么快,但这是由于动态缓存的额外开销,编译器静态缓存)。

Dynamic.InvokeGet(d,"value2");

Dynamitey is an open source .net std library, that let's you call it like the dynamic keyword, but using the a string for the property name rather than the compiler doing it for you, and it ends up being equal to reflection speedwise (which is not nearly as fast as using the dynamic keyword, but this is due to the extra overhead of caching dynamically, where the compiler caches statically).

Dynamic.InvokeGet(d,"value2");
新人笑 2024-10-23 15:49:13

获取属性的 settergetter 的最简单方法,适用于任何类型,包括 dynamicExpandoObject > 是使用 FastMember 这也恰好是最快的方法(它使用发射)。

您可以获取基于给定类型的 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");

The easiest method for obtaining both a setter and a getter for a property which works for any type including dynamic and ExpandoObject is to use FastMember which also happens to be the fastest method around (it uses Emit).

You can either get a TypeAccessor based on a given type or an ObjectAccessor based of an instance of a given type.

Example:

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");
你没皮卡萌 2024-10-23 15:49:13

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

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

一旦转换为 IDictionary,您就可以访问 .Item 和 .ContainsKey 等有用的方法

Much of the time when you ask for a dynamic object, you get an ExpandoObject (not in the question's anonymous-but-statically-typed example above, but you mention JavaScript and my chosen JSON parser JsonFx, for one, generates ExpandoObjects).

If your dynamic is in fact an ExpandoObject, you can avoid reflection by casting it to IDictionary, as described at http://msdn.microsoft.com/en-gb/library/system.dynamic.expandoobject.aspx.

Once you've cast to IDictionary, you have access to useful methods like .Item and .ContainsKey

一刻暧昧 2024-10-23 15:49:13

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

使用 JsonConvert 序列化对象:

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

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

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

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

The GetProperty/GetValue does not work for Json data, it always generate a null exception, however, you may try this approach:

Serialize your object using JsonConvert:

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

Then access it directly casting it back to string:

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

It may work straight applying the Convert.ToString(request)["DynamicFieldName"], however I haven't tested.

月野兔 2024-10-23 15:49:13

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

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

To get properties from dynamic doc
when .GetType() returns null, try this:

var keyValuePairs = ((System.Collections.Generic.IDictionary<string, object>)doc);
var val = keyValuePairs["propertyName"].ToObject<YourModel>;
月依秋水 2024-10-23 15:49:13

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

返回一个 PropertyInfo 对象。

那么就这样做

propertyInfo.GetValue(d)

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

returns a PropertyInfo object.

So then do

propertyInfo.GetValue(d)
一张白纸 2024-10-23 15:49:13

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

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

        }


    }

This is the way i ve got the value of a property value of a dinamic:

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

        }


    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文