Custom Dynamic Behavior using DynamicObject





5.00/5 (2 votes)
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. Thebinder.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. Thebinder.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. Thebinder.Name
property provides the name of the member.Args[]
arguments passed along with the methodResult
value returned from the method after executionReturns 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