Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Custom Dynamic Behavior using DynamicObject

0.00/5 (No votes)
17 Sep 2019 1  
Custom dynamic behavior using DynamicObject

Introduction

The DynamicObject class enables the developer to implement dynamic types or dynamic behavior at runtime. The developer cannot create an instance of this class directly. Instead, he needs to extend to have the desired behavior. For example, please see below minimal steps for a class with a dynamic behavior to create and use properties and methods.

dynamic d; // defines field d as dynamic and the type of the d determines at runtime
d = 10;    // at runtime, type of the d resolved as Int.32

Step 1: Extend DynamicObject Class

Create a class and extend with DynamicObject.

class Employee : DynamicObject // create a class and extend with DynamicObject 
{
    private IDictionary<string, object=""> _members = new Dictionary<string, object="">();
}

Step 2: Define a Dictionary

Define a dictionary to store dynamic objects such as methods, properties, and their values.

class Employee : DynamicObject // create a class and extend with DynamicObject 
{
    private IDictionary<string, object=""> _members = new Dictionary<string, object=""<();
}

Step 3. Implement TrySetMember

TrySetMember provides the implementation to create methods, properties, and their values. For example, to set a property or method to employee class such as:

employee.Id = 1007; // create a property Id and assign value 1007 at runtime

employee.GetSalary = (Action)(() => 
  { Console.WriteLine("Method is Invoked"); }); // Create a method GetSalary at runtime.

Then, override method TrySetMember of DynamicObject. In our example implementation, the method TrySetMember creates a property ‘Id’ assign value 1007 at runtime to the line employee.Id = 1007.

class Employee: DynamicObject // create a class and extend with DynamicObject 
{
    private IDictionary<string, object=""> _members = new Dictionary<string, object="">();

    
    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
         _members[binder.Name] = value;
         return true;
    }
}

class Program
{
    static void Main(string[] args)
    {
         dynamic employee = new Employee();
         employee.Id = 1007;         // create a property Id and assign value 1007 at runtime
         employee.Name = "Bhupathi"; // create a property Name 
                                     // and assign value "Bhupathi" at runtime
         employee.GetSalary = (Action)(() => 
          { Console.WriteLine("Method is Invoked"); }); // Create a method GetSalary at runtime
         Console.ReadKey();
    }
} 

TrySetMember Parameters

  • Binder provides information about the object that is trying to set the value. The binder.Name property provides the name of the member.
  • Result is the value to set for the dynamic object.
  • Returns true if the operation is successful; otherwise, false

Step 4. Implement TryGetMember

TryGetMember provides the implementation to get the value of the property. For example, var when we are trying to access employee.Id, then DLR uses the language binder to look for the static definition. If there is no property, then DLR calls the TryGetMember.

class Employee: DynamicObject // create a class and extend with DynamicObject 
{
    private IDictionary<string, object=""> _members = new Dictionary<string, object="">();
    
    public override bool TrySetMember(SetMemberBinder binder, object value) {...}
    
    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
          return _members.TryGetValue(binder.Name, out result);
    }
}

class Program
{
    static void Main(string[] args)
    {
          dynamic employee = new Employee();
          employee.Id = 1007; // create a property Id and assign value 1007 at runtime
          employee.Name = "Bhupathi"; // create a property Name and 
                                      // assign value "Bhupathi" at runtime
          employee.GetSalary = (Action)(() => 
           { Console.WriteLine("Method is Invoked"); }); // Create a method 
                                                         // GetSalary at runtime

          Console.WriteLine("List of values in employee object");
          Console.WriteLine($"Employee Id : 
            {employee.Id}"); // retrieves the value of property id in employee object
          Console.WriteLine($"Employee Name : {employee.Name}"); // retrieves the value 
                                                                 // of property Name in 
                                                                 // employee object
          Console.ReadKey();
   }
}

In our example for the line int eid = employee.Id, TryGetMember returns the value of the Id.

TryGetMember Parameters

  • Binder provides information about the object that is trying to retrieve the value. The binder.Name property provides the name of the member.
  • Result is the return value of the dynamic object that is trying to retrieve a value.
  • Returns true if the operation is successful; otherwise, false.

Step 5. Implement TryInvokeMember

TryInvokeMember provides the implementation to invoke a method.

class Employee: DynamicObject // create a class and extend with DynamicObject 
{
    private IDictionary<string, object=""> _members = new Dictionary<string, object="">();
    
    public override bool TrySetMember(SetMemberBinder binder, object value) {...}
    
    public override bool TryGetMember(GetMemberBinder binder, out object result) {...}

    public override bool TryInvokeMember
           (InvokeMemberBinder binder, object[] args, out object result)
    {
         Type dictType = typeof(Dictionary<string, object="">);
         try
         {
              result = dictType.InvokeMember(
                       binder.Name,
                       BindingFlags.InvokeMethod,
                       null, _members, args);
              return true;
          }
          catch
          {
              return false;
          }
     }
}

class Program
{
     static void Main(string[] args)
     {
          dynamic employee = new Employee();
          employee.GetSalary = (Action)(() => 
          { Console.WriteLine("Method is Invoked"); }); // Create a method 
                                                        // GetSalary at runtime
          employee.GetSalary();
          Console.ReadKey();
      }
}  

In our example, employee.GetSalary(); GetSalary method will be invoked with null arguments.

TryInvokeMember Parameters

  • Binder provides information about the object or method that is trying to invoke. The binder.Name property provides the name of the member.
  • Args[] arguments passed along with the method
  • Result value returned from the method after execution
  • Returns true if the operation is successful; otherwise, false

ExpandoObject

ExpandoObject is a fully featured class and implementation of IDynamicMetaObjectProvider, which is part of the dynamic library in .NET. ExpandoObject is very useful when you do not require any custom behavior to your dynamic class.

Conclusion

Dynamic objects emit code at runtime and are very expensive. Do not use dynamic objects unless it is required to, such as consuming COM API, for example, Office API.

History

  • 17th September, 2019: Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here