231

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!

TimDog
  • 8,758
  • 5
  • 41
  • 50
  • 31
    Note that this is not the intended purpose of "dynamic" and that this scenario does not work any better with "dynamic" than it does with "object". "dynamic" makes it possible to access properties when the *name* of the property is known at compile time but the *type* is not. Since you know neither the name nor the type at compile time, dynamic is not going to help you. – Eric Lippert Feb 09 '11 at 00:24
  • Possibly related: http://stackoverflow.com/questions/5877251/lookup-property-in-object-graph-via-a-string. – DuckMaestro Oct 06 '14 at 17:17
  • 3
    @EricLippert I know this question is old but just to make a comment in case someone see it in the future. In some cases you can't choose whether to use dynamic or object (for instance when using the JSON parser) and you still might want to get the properties from a string (from a config file for instance) so this use is not that unusual as one might initially think. – Pedrom Jan 05 '17 at 13:27

15 Answers15

284

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);
Adam Robinson
  • 182,639
  • 35
  • 285
  • 343
  • 5
    I'm getting a `'d.GetType().GetProperty("value2").GetValue(d)' threw an exception of type 'System.Reflection.TargetInvocationException' dynamic {System.Reflection.TargetInvocationException}` in the watch window with that..? – TimDog Feb 08 '11 at 23:04
  • 6
    Think GetValue needs an additional parameter - e.g. d.GetType().GetProperty("value2").GetValue(d, null) – dommer Feb 08 '11 at 23:09
  • Adam, you were correct -- with dommer's `,null` it worked correctly...thanks for your help. – TimDog Feb 08 '11 at 23:16
  • @dommer, @TimDog: D'oh! Right, I forgot about the indexer parameter, since only VB.NET supports parameterized properties. Fixed. – Adam Robinson Feb 08 '11 at 23:17
  • 3
    Will this work on a true dynamic ExpandoObject rather than an anonymous type? Since `new {}` creates a real anonymous type with defined properties, calling GetType/GetProperty makes sense, but what about ExpandoObject, which if you call GetType, you'll get a type that has the properties of ExpandoObject, but not necessarily its dynamic properties. – Triynko Jan 27 '14 at 21:24
  • 21
    -1. This only work with simple .NET objects that were casted to dynamic. It will not work with any custom dynamic object like Expando or ViewBag used ASP.NET MVC – Philipp Munin Jan 28 '15 at 19:12
  • 11
    this is what works with Expando Object: (((IDictionary)x))["value1"] – Michael Bahig Sep 13 '15 at 17:50
  • 1
    This also won't work with `PSObject`. I am looking for a solution that will work for any dynamic object. – felixfbecker Sep 02 '18 at 20:28
  • T H A N K S ! ! ! ! ! ! ! ! ! – Robert Green MBA Oct 21 '20 at 19:49
  • This can be done for every instance – gsscoder Mar 07 '22 at 06:01
47
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);
}
IS4
  • 11,945
  • 2
  • 47
  • 86
  • 2
    Just wanted to mention that the VisualBasic version is not equivalent to your original 'GetProperty' version (the GetProperty actually invokes the dynamic GetMember, which works even on Python objects in IronPython). – Trevor Sundberg Nov 27 '14 at 00:29
  • ➕1 this worked for private properties when both FastMember and HyperDescriptor would not – Chris Marisic Mar 21 '16 at 18:10
  • @IllidanS4 when you compared the `CallSite` code vs `CallByName` code did you compare the two while caching the `CallSite` instance? I would suspect the cost of your first method is almost purely the activation of the `Binder` and `CallSite`, not the invocation of `Target()` – Chris Marisic Mar 21 '16 at 18:17
  • I wonder if you can check with this if a property **exists** before trying to get its value?? -- So far this is the only thing that got close to what I need (since I'm working with COM objects, which happen to be dynamic by themselves) – Daniel Möller Mar 10 '17 at 14:58
  • @Daniel For some objects, you can call `IDispatch::GetTypeInfo` and `ITypeInfo::GetFuncDesc`. Although it might not work for some types. – IS4 Mar 10 '17 at 16:18
29

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");
jbtule
  • 31,383
  • 12
  • 95
  • 128
13

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");
MaYaN
  • 6,683
  • 12
  • 57
  • 109
9

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

Francis Norton
  • 674
  • 1
  • 8
  • 16
  • Unfortunately, having to cast to IDictionary and using TryGetValue for example, results in a plain old object being returned. You cannot take advantage of implicit operators at that point, since they are only considered at compile time. For example, if I had an Int64Proxy class with implicit conversion to Int64?, then `Int64? i = data.value; //data is ExpandoObject` would automatically lookup and call the implicit operator. On the other hand, if I had to use IDictionary to test whether "value" field exists, I'd get an object back that will not cast without error to Int64?. – Triynko Jan 27 '14 at 21:27
9

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.

Anderson
  • 134
  • 1
  • 6
  • 3
    This method generates the error: error CS0021: Cannot apply indexing with [] to an expression of type 'object'. Use `new JavaScriptSerializer().Deserialize(json);` to get to the "properties" in the way you suggested – Kris Kilton Feb 16 '18 at 22:27
  • This is worked like a charm! 20-Nov-2020 Thanks dude – Bcktr Nov 20 '20 at 04:15
8

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>;
Trevor Reid
  • 3,310
  • 4
  • 27
  • 46
yzhai bruin
  • 81
  • 1
  • 1
6

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

returns a PropertyInfo object.

So then do

propertyInfo.GetValue(d)
James Gaunt
  • 14,631
  • 2
  • 39
  • 57
  • 3
    thanks, this was the correct answer, but as mentioned above, the `GetValue(d)` needs to be `GetValue(d,null)` – TimDog Feb 08 '11 at 23:18
5

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

        }


    }
3

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

kudzanayi
  • 61
  • 5
2

In .Net core 3.1 you can try like this

d?.value2 , d?.value3
vyeluri5
  • 487
  • 5
  • 8
  • 18
0

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.

Efreeto
  • 2,132
  • 1
  • 24
  • 25
  • FYI Difference between Property and Field in C# 3.0+: https://stackoverflow.com/a/653799/2680660 – Efreeto Jun 08 '20 at 23:37
0

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.

Tore Aurstad
  • 3,189
  • 1
  • 27
  • 22
0

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

George Paoli
  • 2,257
  • 1
  • 24
  • 29
0

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

Ali Adravi
  • 21,707
  • 9
  • 87
  • 85