How to set a property of a C# 4 dynamic object whe

2020-05-18 04:55发布

I'm looking for a way to modify properties on a dynamic C# 4.0 object with the name of the property known only at runtime.

Is there a way to do something like (ExpandoObject is just used as an example, this could be any class that implements IDynamicMetaObjectProvider):

string key = "TestKey";
dynamic e = new ExpandoObject();
e[key] = "value";

Which would be equivalent to:

dynamic e = new ExpandoObject();
e.TestKey = "value";

Or is the only way forward reflection?

5条回答
冷血范
2楼-- · 2020-05-18 05:07

Paul Sasik answered a similar question at C# 4.0 Dynamic vs Expando... where do they fit?

using System;
using System.Dynamic;

class Program
{
    static void Main(string[] args)
    {
        dynamic expando = new ExpandoObject();
        var p = expando as IDictionary<String, object>;
        p["A"] = "New val 1";
        p["B"] = "New val 2";

        Console.WriteLine(expando.A);
        Console.WriteLine(expando.B);
    }
}
查看更多
家丑人穷心不美
3楼-- · 2020-05-18 05:07

fast-member may fit the bill - it looks like it generates the IL on the fly, but caches it so it's really fast after the first use.

查看更多
迷人小祖宗
4楼-- · 2020-05-18 05:20

My open source framework Dynamitey has methods for invoking based on string names using the DLR. It does the work of caching binding sites and streamlines it down to one method call. it also runs faster than reflection on non-dynamic objects too.

Dynamic.InvokeSet(e, "TestKey", "value");
查看更多
Explosion°爆炸
5楼-- · 2020-05-18 05:24

To add to Jonas' answer, you don't have to create a new var p. Use this approach instead:

using System;
using System.Dynamic;

class Program
{
    static void Main(string[] args)
    {
        dynamic expando = new ExpandoObject();
        ((IDictionary<String, object>)expando)["A"] = "New val 1";
        ((IDictionary<String, object>)expando)["B"] = "New val 2";

        Console.WriteLine(expando.A);
        Console.WriteLine(expando.B);
    }
}
查看更多
干净又极端
6楼-- · 2020-05-18 05:27

Not very easily, no. Reflection doesn't work, since it assumes a regular type model, which is not the full range of dynamic. If you are actually just talking to regular objects, then just use reflection here. Otherwise, I expect you may want to reverse-engineer the code that the compiler emits for a basic assignment, and tweak it to have a flexibly member-name. I'll be honest, though: this isn't an attractive option; a simple:

dynamic foo = ...
foo.Bar = "abc";

translates to:

if (<Main>o__SiteContainer0.<>p__Site1 == null)
{
    <Main>o__SiteContainer0.<>p__Site1 = CallSite<Func<CallSite, object, string, object>>.Create(Binder.SetMember(CSharpBinderFlags.None, "Bar", typeof(Program), new CSharpArgumentInfo[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.Constant | CSharpArgumentInfoFlags.UseCompileTimeType, null) }));
}
<Main>o__SiteContainer0.<>p__Site1.Target(<Main>o__SiteContainer0.<>p__Site1, foo, "abc");

If you want an approach that works for both dynamic and non-dynamic objects: FastMember is handy for this, and works at either the type or object level:

// could be static or DLR 
var wrapped = ObjectAccessor.Create(obj); 
string propName = // something known only at runtime 
Console.WriteLine(wrapped[propName]);

available on Nuget, and heavily optimised for both dynamic and non-dynamic scenarios.

查看更多
登录 后发表回答