using Dynamic to add methods?

Royi Namir picture Royi Namir · May 9, 2012 · Viewed 7.6k times · Source

Im trying to add function on runtime , something like that :

static void Main()
{
 dynamic d = new Duck();
 d.Quack =(Action) (() => Console.WriteLine("1")); //decalre a new method on runtime ??
 d.Quack(); 

}

public class Duck : System.Dynamic.DynamicObject
{
   //...
}

'UserQuery.Duck' does not contain a definition for 'Quack'

Isnt dynamic should allow me to do it ?

does brand new ExpandoObject is the only solution ?

i have my Duck class already. how can i make it Expando ? - can i make duck act like expando ?

Answer

gideon picture gideon · May 9, 2012

You can't add properties of any type (even functions) to an existing type.

dynamic d = new Duck();
d.Quack = "Quack";//fails because there is no such property on duck

You could use ExpandoObject though:

dynamic d = new ExpandoObject();
d.Quack = (Action)(() => System.Console.WriteLine("1"));
d.Quack(); 

Don't be confused with what the dynamic type does.

void speak(dynamic d)
{
 d.Quack();//this is NOT checked at compile time
}

Now I can do : speak(new Duck()); and speak(new Goose()); , it will compile and run if both Duck and Goose have the method Quack(), if they don't, it raises an exception. (The same one you get)

When you call a method/property on a dynamic type, it only resolves that at runtime and doesn't do a compiler check.

The ExpandoObject allows you to create properties on the fly.


To answer your question on the comment, the way I see it is, if you need your own class which needed the ability to create new properties you could inherit from DynamicObject. Like this (adapted from this msdn page):

class DynamicDuck : DynamicObject 
{
      Dictionary<string, object> dictionary
           = new Dictionary<string, object>();
    public int Count
    {
        get
        {
           return dictionary.Count;
        }
     }
    public override bool TryGetMember(
          GetMemberBinder binder, out object result)
    {
          string name = binder.Name.ToLower();
            return dictionary.TryGetValue(name, out result);
    }
    public override bool TrySetMember(
         SetMemberBinder binder, object value)
    {
          dictionary[binder.Name.ToLower()] = value;
          return true;
    }
 }

Then you could do:

dynamic d = new DynamicDuck();
d.firstname = "Gideon";
d.Quack = (Action)(() => System.Console.WriteLine("Quack"));