C# 4, More scrappy thoughts on dynamic

NB: This comes from experimentation rather than being some kind of C# guru so apply a pinch of salt to anything and everything and I’ll correct if someone tells me I messed up 🙂

I’m also not sure I can add a lot around this dynamic feature in C# 4 but I’ve been thinking about some demos around it today so I thought I’d jot it all down rather than go at it piecemeal which is what I was doing here and here.

I’m starting to think that life in C# 3.0 was pretty simple 🙂 I could write code such as;

class MyClass
{
  public int Add(int x, int y)
  {
    return (x + y);
  }
}
class Program
{
  static void Main(string[] args)
  {
    object o = new MyClass();
    int result = o.Add(10, 20);
  }
}

and it won’t compile because the static type of o is object and object doesn’t have a method called Add on it.

If I move to C# 4 and drop in the new type dynamic then the compiler is prepared to give me “the benefit of the doubt” and attempt to find the right shape of method on o at runtime instead. So, I can write;

class MyClass
{
  public int Add(int x, int y)
  {
    return (x + y);
  }
}
class Program
{
  static void Main(string[] args)
  {
    dynamic o = new MyClass();
    int result = o.Add(10, 20);
  }
}

and that’ll both compile and run fine and it’s not just methods that the compiler can do this stuff for – it’s properties, indexers and so on as well.

This “treat as dynamic” keyword also has meaning for parameters, member variables and so on (i.e. it’s not just for local stack-based variables like var is) so, again, this code won’t compile;

  static void Main(string[] args)
  {
    CallFn(new MyClass());
  }
  static int CallFn(object o)
  {
    return (o.Add(10, 20));
  }

but this code will (and will work);

  static void Main(string[] args)
  {
    CallFn(new MyClass());
  }
  static int CallFn(dynamic o)
  {
    return (o.Add(10, 20));
  }

and that same theme follows on to properties/return values and so on in that this code;

  static void Main(string[] args)
  {
    GetObject().Add(10, 20);
  }
  static object GetObject()
  {
    return (new MyClass());
  }

won’t compile again whereas this code does;

  static void Main(string[] args)
  {
    GetObject().Add(10, 20);
  }
  // static dynamic? uh-oh!
  static dynamic GetObject()
  {
    return (new MyClass());
  }

because the compiler treats the return value of GetObject() as dynamic rather than just object.

Methods are being resolved at run-time in a way that tries to match what is intuitive to someone used to compile-time resolution as in;

class MyClass
{
  public int Add(int x, int y)
  {
    return (x + y);
  }
  public int Add(double x, double y)
  {
    return ((int)(x + y));
  }
}
class Program
{
  static void Main(string[] args)
  {
    dynamic d = new MyClass();
    d.Add(10, 20);
    d.Add(10.0, 20.0);
  }
}

and so here we first get a call to the (int,int) overload and then a call to the (double,double) overload as we’d expect – this is based on the static compile time type of the parameters that we’re passing. This is more obvious if we do something like this;

class Base
{
}
class Derived : Base
{
}
class MyClass
{
  public int Add(int x, int y)
  {
    return (x + y);
  }
  public int Add(double x, double y)
  {
    return ((int)(x + y));
  }
  public int Add(Base b1, Base b2)
  {
    return (0);
  }
  public int Add(Derived d1, Derived d2)
  {
    return (0);
  }
}
class Program
{
  static void Main(string[] args)
  {
    dynamic d = new MyClass();
    d.Add(10, 20);
    d.Add(10.0, 20.0);
    Base b = new Derived();
    d.Add(b, b);  // This calls the (Base,Base) method.
    d.Add((Derived)b, (Derived)b); // This calls the (Derived,Derived) method
  }
}

so here we get a call to all 4 overloads in declaration order – that is whilst the method call is being dispatched dynamically to the object referenced by d the resolution is using the compile-time type of the parameters at the call-site rather than interrogating them for run-time type. But…I can get them interrogated for run-time type by doing;

  static void Main(string[] args)
  {
    dynamic d = new MyClass();
    Base b = new Derived();
    d.Add((dynamic)b, (dynamic)b); // This calls the (Derived,Derived) method.
    d.Add((dynamic)b, (dynamic)b); // This calls the (Derived,Derived) method
  }

There’s a fip-side to all this though. Even method calls which look to be static can easily become dynamic in the face of a dynamic parameter as in;

class Base
{
}
class Derived : Base
{
}
class MyClass
{
}
class Program
{
  static void Main(string[] args)
  {
    dynamic d = new MyClass();
    Fn(d);
    d = new Base();
    Fn(d);
    d = new Derived();
    Fn(d);
  }
  public static void Fn(MyClass c)
  {
  }
  public static void Fn(Base b)
  {
  }
  public static void Fn(Derived d)
  {
  }

}

where the compiler can’t know which overload of Fn to call until runtime so it has to do dynamic resolution which once again calls all 3 overloads of Fn in order, resolving the overload based on the run-time type of the parameter.

That doesn’t mean though that everything needs to be resolved dynamically. For example;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

class Base
{
  public void Foo(object o) { }
  public void Foo(int i) { }
}
class Derived : Base
{
  public void Foo(MyClass c) { }
}
class MyClass
{
}
class Program
{
  static void Main(string[] args)
  {
    Base b = new Derived();
    dynamic d = 101;
    b.Foo(d);
    d = new MyClass();
    b.Foo(d);
  }
}

in this example we use a dynamic value as a parameter but the object that we make the method calls on is typed as Base and so the candidate methods for resolution become the;

Foo(object o)

Foo(int i)

overloads. The fact that b is actually an instance of Derived and that there’s a better matching method on Derived doesn’t matter because the type of the variable where we did the invocation of the method was statically typed to be Base and so that was what was considered for resolution.

If I wanted the run-time-type of b to be considered then I could do;

class Base
{
  public void Foo(object o) { }
  public void Foo(int i) { }
}
class Derived : Base
{
  public void Foo(MyClass c) { }
}
class MyClass
{
}
class Program
{
  static void Main(string[] args)
  {
    Base b = new Derived();
    dynamic d = 101;
    b.Foo(d);
    d = new MyClass();
    ((dynamic)b).Foo(d);
  }
}

and then this code makes a call to Base.Foo(int) and then a call to Derived.Foo(MyClass) because that’s a better match for the method we’re calling than Base.Foo(object).

All of this has so far been based around reflection. If I want to get involved in the dynamic dispatch mechanism then I can implement IDynamicObject on my object and then do something in response to the various calls that come in to that interface.

I’m not on the VS2010 CTP right now so this next chunk of code is really pseudo-code. If I imagine that I’ve a BaseDynamicObject class which implements a bunch of IDynamicObject for me around reflection and leaves me to override the pieces of the implementation that I want to override then I might write something like this (again pseudo-code) to hook into a dynamic method dispatch;

class MyClass : BaseDynamicObject
{
  public override bool TryInvokeMember(
    InvokeMemberBinder binder, 
    object[] args, 
    out object result)
  {
    Console.WriteLine("Someone called method [{0}]", binder.Name);
    if (args != null)
    {
      Console.WriteLine("\tWith arguments");
      foreach (object o in args)
      {
        Console.WriteLine("\t\t[{0}]", o);
      }
    }
    Console.WriteLine("Ignoring the method call and pretending return value is null");
    result = null;
    return (true);
  }
}
class Program
{
  static void Main(string[] args)
  {
    dynamic d = new MyClass();
    d.Foo("One", 101, 2.5f);
    Console.ReadLine();
  }
}

and then that can springboard into whatever dynamic dispatch mechanism I like.