8

I'm looking for a way to add members dynamically to an dynamic object. OK, I guess a little clarification is needed...

When you do that :

dynamic foo = new ExpandoObject();
foo.Bar = 42;

The Bar property will be added dynamically at runtime. But the code still refers "statically" to Bar (the name "Bar" is hard-coded)... What if I want to add a property at runtime without knowing its name at compile time ?

I know how to do this with a custom dynamic object (I actually blogged about it a few months ago), using the methods of the DynamicObject class, but how can I do it with any dynamic object ?

I could probably use the IDynamicMetaObjectProvider interface, but I don't understand how to use it. For instance, what argument should I pass to the GetMetaObject method ? (it expects an Expression)

And by the way, how do you perform reflection on dynamic objects ? "Regular" reflection and TypeDescriptor don't show the dynamic members...

Any insight would be appreciated !

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • In C# 6.0, *may be* you can write it like `foo.$Bar = 42;` :) Not sure if it's allowed for dynamic... – nawfal Jul 21 '14 at 12:25
  • @nawfal, actually, that feature has been dropped... but anyway, `foo.$Bar` is just shorthand for `foo["Bar"]` – Thomas Levesque Jul 21 '14 at 12:26
  • Thomas, didn't know about the feature being dropped (I'm glad about it), but oh yes, for a moment I overlooked the real requirement of your q. – nawfal Jul 21 '14 at 12:28

4 Answers4

9

What you want is similar to Python's getattr/setattr functions. There's no built in equivalent way to do this in C# or VB.NET. The outer layer of the DLR (which ships w/ IronPython and IronRuby in Microsoft.Scripting.dll) includes a set of hosting APIs which includes an ObjectOperations API that has GetMember/SetMember methods. You could use those but you'd need the extra dependency of the DLR and a DLR based language.

Probably the simplest approach would be to create a CallSite w/ one of the existing C# binders. You can get the code for this by looking at the result of "foo.Bar = 42" in ildasm or reflector. But a simple example of this would be:

object x = new ExpandoObject();
CallSite<Func<CallSite, object, object, object>> site = CallSite<Func<CallSite, object, object, object>>.Create(
            Binder.SetMember(
                Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags.None,
                "Foo",
                null,
                new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) }
            )
        );
site.Target(site, x, 42);
Console.WriteLine(((dynamic)x).Foo);
Dino Viehland
  • 6,478
  • 20
  • 25
  • I need to take some time to make sure I really understand what this code is doing, but anyway it's working fine... Thanks ! – Thomas Levesque Jan 17 '10 at 16:42
  • Would it be possible to do this < .Net 4.0 using the DLR? It appears that the use of dynamic would preclude this. – Firestrand Feb 03 '10 at 17:33
  • The method above should not work properly. Setter call site requires 2 argument infos to be provided instead of 1. The correct definition will be: new[] { CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null), CSharpArgumentInfo.Create(CSharpArgumentInfoFlags.None, null) } – Denis Vuyka May 12 '11 at 14:12
5

The opensource framework Dynamitey will do this (available via nuget). It encapsulates while still caching the call site and binder code that @Dino-Viehland used.

Dynamic.InvokeSet(foo,"Bar",42);

It can also call many other kinds of c# binder too.

jbtule
  • 31,383
  • 12
  • 95
  • 128
5

ExpandoObject implements IDictionary<string,object> albeit explicitly. What this means is that you can simply cast the ExpandoObject to IDictionary<string,object> and manipulate the dictionary.

dynamic foo = new ExpandoObject();
foo.Bar = 42;
food = (IDictionary<string,object>)foo;
food["Baz"] = 54
Uffe Seerup
  • 59
  • 1
  • 1
  • Thanks! Indeed ExpandoObject implements IDictionary, as I realized afterwards, so it is clearly the simplest solution in that case. However the scope of my question was wider: I was looking for a solution that would work with any dynamic object, not just ExpandoObject – Thomas Levesque Aug 14 '11 at 09:11
1

I know this is quite an old post, but I thought I'd pass along Miron Abramson solution on how you can create your own type and add properties at runtime -- in case anyone else out there is looking for something similar.

JoBot
  • 131
  • 1
  • 7