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;
d = 10;
Step 1: Extend DynamicObject Class
Create a class and extend with DynamicObject
.
class Employee : 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
{
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;
employee.GetSalary = (Action)(() =>
{ Console.WriteLine("Method is Invoked"); });
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
{
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;
employee.Name = "Bhupathi";
employee.GetSalary = (Action)(() =>
{ Console.WriteLine("Method is Invoked"); });
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
{
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;
employee.Name = "Bhupathi";
employee.GetSalary = (Action)(() =>
{ Console.WriteLine("Method is Invoked"); });
Console.WriteLine("List of values in employee object");
Console.WriteLine($"Employee Id :
{employee.Id}");
Console.WriteLine($"Employee Name : {employee.Name}");
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
{
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"); });
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