Dynamic Reception in C#

Posted: November 22, 2009 in .NET, C#
Tags: ,

In my last couple of posts I had discussed about the new dynamic keyword introduced in C# 4.0 and the how the DLR integrates with CLR to provide the dynamic programming services.In this post I will discuss about the System.Dynamic.DynamicObject class and how we can implement dynamic receivers in C#.Dynamic Receivers allows the client code to make a method call that is not defined in the class.Take a look at the following C# code:


static void Main(string[] args)
{
dynamic e = new Employee();
e.FindByFirstNameAndLastName("Sankarsan","Bose");
Console.Read();
}

If the class Employee does not have the FindByFirstNameAndLastName method defined in it then there will be no compile time error but at runtime we will get a RuntimeBinderException with message:

‘DynamicReceiver.Employee’ does not contain a definition for ‘FindByFirstNameAndLastName’

Dynamic Reception is the technique by which we try to handle a undefined method situation.

Before we get into how to implement this case above let’s take a look into the System.Dynamic.DynamicOject class and some of it’s important methods.

DynamicObject is the base class used for providing runtime dynamic behavior and the classes which need such a behavior has to inherit this class.This class cannot be instantiated and used on it’s own.

The three important methods are:

  • public virtual bool TryGetMember(GetMemberBinder binder,out Object result) – This operation is invoked when any dynamic property is accessed on the dynamic object.The subclasses can override this method to provide specialized logic for member access.
    • GetMemberBinder class provides the abstraction for dynamic get member operation.The Name property of GetMemberBinder provides the name of the member/property we are trying to access.
    • The out parameter result contains the value of the member we are trying to access
    • The method returns true if the member access is successful.
  • public virtual bool TrySetMember(SetMemberBinder binder,Object value) – This operation is invoked when we are trying to set value of a dynamic member and subclasses can override this method to provide specialized logic for member access.
    • SetMemberBinder provides abstraction of a set member operation at the callsite and Name property returns the name of the member whose value we are trying to set.
    • The parameter value contains the value we are trying to set to the member.
    • Returns true if the value is successfully set.
  • public virtual bool TryInvokeMember(InvokeMemberBinder binder,Object[] args,out Object result) – This operation is invoked when we are trying to call a dynamic method on a dynamic object.The subclasses can override this method to provide specialized logic for member access.
    • InvokeMemberBinder class provides the abstraction for dynamic method invocation at the callsite.The Name property of InvokeMemberBinder provides the name of the method we are trying to invoke.
    • The parameter args contains the values for input parameters of the method to be invoked.
    • The out parameter result contains the return value of the method we are trying to invoke.
    • The method returns true if the method invocation is successful.

Let’s again go back to code.Our Employee class is defined as follows:


public class Employee : DynamicObject
{
public string FirstName { get; set; }
public string LastName { get; set; }

private Employee[] FindBy(string firstName=null,string lastName=null)
{
Console.WriteLine("Find Employees with Criteria::\n");
Console.WriteLine("FirstName with value={0}\n",firstName);
if (lastName != null)
{
Console.WriteLine("And\nLastName with value={0}\n", lastName);
}
return null;
}
}

We have no method named FindByFirstNameAndLastName.So how to tackle this situation?.Let’s set a rule.Any dynamic find method on the Employee object should follow the convention that name will start with “FindBy” followed the by property name to be used as criteria of the query.e.g. FindByFirstName, FindByLastName and FindByFirstNameAndLastName.We will implement this rule by overriding the TryInvokeMember as shown below:


public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
bool retVal = false;
result = null;
string methodName = binder.Name;
if (methodName.StartsWith("FindBy"))
{
string param = methodName.Substring(6);
if (param.Contains("And"))
{
string[] paramlist = param.Split("And".ToCharArray(),StringSplitOptions.RemoveEmptyEntries);
if(paramlist[0]=="FirstName" && paramlist[1]=="LastName")
{
result = FindBy(args[0].ToString(), args[1].ToString());
retVal = true;
}
}
else
{

if (param == "FirstName")
{
result = FindBy(firstName: args[0].ToString());
retVal = true;
}
else if (param == "LastName")
{
result = FindBy(lastName: args[0].ToString());
retVal = true;
}
}
}
return retVal;
}

Now the question might come up why I am not exposing the FindBy method as public?Of course that can be done.But this approach is more flexible, productive and code with method names like FindByFirstName,FindByLastName is more readable and easier to understand.The disadvantage here is the method naming convention is nowhere enforced in code and developer must have that knowledge from documentation.

NOTE: This code is written Visual Studio Beta 2.

Advertisements
Comments
  1. Dynamic Reception in C# « Sankarsan’s Journal…

    Thank you for submitting this cool story – Trackback from DotNetShoutout…

  2. Dynamic Reception in C# « Sankarsan’s Journal…

    Thank you for submitting this cool story – Trackback from PimpThisBlog.com…

  3. Dynamic Reception in C# « Sankarsan’s Journal…

    Thank you for submitting this cool story – Trackback from Servefault.com…

  4. […] This post was mentioned on Twitter by Sankarsan Bose, Sankarsan Bose. Sankarsan Bose said: RT @tweetmeme http://bit.ly/0874HoA […]

  5. Ecko says:

    nice article, thanks,,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s