Introduction (How to use the dynamic keyword)
The dynamic
keyword is a new feature in C# 4.0. A dynamic object's type may not be known until runtime and that means the members of that dynamic object and the signatures will not be known at runtime. It also means that the calls to the methods of that dynamic object will be dispatched dynamically. When we say dynamically dispatched, it may fail if it does not find a given method at run time. It bypasses the compile time type checking and dynamic operations will be resolved at runtime.
The implementation of the dynamic
keyword happens using the component DLR (Dynamic Language Runtime).
How to use
We use the keyword dynamic
to create dynamic object and use it like any other type.
- In the above figure, here is how we are using the
dynamic
keyword:
- Make the return type of the method as dynamic.
- Pass parameter of dynamic type in place of a variable of integer type.
- Declare a variable of dynamic type where the value is a string.
- Declare a variable of dynamic type where the value is a float.
- Declare a variable of dynamic type where the value is a reference type.
Compiler behavior
A dynamic type bypasses compile time type checking. At compile time, it records the metadata about the various calls such as what it means, and the types of parameters passed in. The metadata can be used during run time to resolve the call, and one of two things is going to happen: it gets dynamically dispatched or it generates a run time error.
Compile time members of a dynamic type variable
As the above code (underlined in red) says, we try to access the members of a dynamic type variable named “dynamicVariableInt
” and it shows that “This operation will be resolved at runtime”.
Now you might ask, what happens if I forcefully give the member names. Refer to the below code and read the comments to see what happens when we forcefully give the member names.
Dynamic objects get resolved at run time, and the second last statement in the above code does not generate a compile time error because the whole operation will be resolved at run time. It throws an error (run time binder exception) at run time.
At run time, it looks for member names implemented and does much more with the classes of the System.Core
-> System.Dynamic
namespace.
Limitations
- Dynamic look up can’t resolve extension methods
- Dynamic method call can’t pass anonymous functions
- Can’t use LINQ over dynamic objects
DLR (Dynamic Language Runtime)
- All the operations happen under dynamic lookup based on component DLR
- It is a normal assembly hosted on CLR (
System.Core
)
- Uses lightweight code gen part of the
System.Reflection
namespace
2. DynamicObject class
Use the System.Dynamic
namespace for making a class available. It allows a base class for specifying dynamic behavior at run time. This class must be inherited from another, and we cannot instantiate it directly. This class allows us to define operations that can be performed on dynamic objects. For example, when we try to get the member of any defined class and if that member is not available, then we can define the operations to be performed at the time of unavailability of members. Further, we have to use syntax like obj.SetPeroperty (“IsActive”, “True”)
to set the property name IsActive
. Instead, we can write this: obj.IsActive = True
. To enable dynamic behavior for an instance of a class derived from the DynamicObject
class, we must use the dynamic
keyword as explained in the Introduction section.
How to use
The SampleDynamicObjectClass
class will allow us to perform dynamic operations by inheriting the DynamicObject
class and by using the methods of the DynamicObject
class. The DynamicObject
class offers many methods to be overridden in the inheriting class and we will see two of those methods: TryInvokeMember
and TrySetMember
.
In the above example, we have created a class SampleDynamicObjectClass
which inherits the DynamicObject
class. Now we will use the TryInvokeMember
and TryGetMember
methods of the DynamicObject
class by overriding the SampleDynamicObject
class.
The main method creates a new object of type SampleDynamicObjectClass
and assigns it to the dynamic object type so that it can call a member dynamically at run time.
TryInvokeMember()
When we call the sampleObj.SampleMethod ()
method, we are calling a method which is not available in the current class and we are calling it so it will go to the TryInvokemember
method and set binder.Name
to the member name which was called, in our case, we have SampleMethod ()
, and we can also pass arguments in the methods which will reflect in the args []
array of the TryinvokeMember ()
method.
The TryInvokeMember()
method will be called when we try to call a method of the SampleDynamicObjectClass
class, which is not available in this class and so we need to perform dynamic operations here (missing method). So, we provide the implementation for operations that invoke a member so this class can override this method to specify dynamic behavior for operations such as calling a method. The Binder
parameter provides information about dynamic operations, binder.Name
provides the name of the member on which the dynamic operation is performed.
For example: sampleObj.SampleMethod ("True")
, where sampleObj
is an instance of this class. Here, binder.Name
returns SampleMethod
.
The result will hold the result of the member invocation.
The return type will hold true
if the operations are successful and if this method returns false
, the run time binder of the language determines the behavior (most of the time, a language specific run time exception is thrown).
TryGetMember()
When we say sampleObj.SampleProperty
, it means we are calling a property which is not available in the current class and we are calling it, so it will go to the TryGetMember
method and set binder.Name
to the member name which was called; in our case, we have SampleProperty
.
The TryGetMember
method will be called when we try to use a property of the SampleDynamicObjectClass
class which is not available and needs to perform dynamic operations. We provide the implementation for operations that get a member so this class can override this method to specify dynamic behavior for operations such as accessing a property. The Binder
parameter provides information about dynamic operations; binder.Name
provides the name of the member on which the dynamic operation is performed.
For example: sampleObj.SampleProperty;
, where sampleObj
is an instance of this class. Here, binder.Name
returns SampleProperty
.
The result will hold the result of member invocation. The return type will hold true
if the operation is successful, and if this method returns false
, the run time binder of the language determines the behavior (most of the time, a run time exception is thrown).
The above explained concept will be used in real time applications as shown below.
3. XmlTraversing using dynamic
Introduction
Based on the above DynamicObject
class concepts explained, we will create an application for XML traversing. It will give an idea of how to use the TryGetMember
method of the DynamicObject
class which is necessary to understand the XmlTraversing application.
Firstly, manipulating XElement
s can be done with methods of the XElement
object and the syntax is:
XElement xmlNavigation = XElement.Parse(@"
<Emails>
<Email>
<Id>1</Id>
<Description>sanjay.patolia@gmail.com</Description>
</Email>
<Email>
<Id>2</Id>
<Description>sanjay.patolia@patni.com</Description>
</Email>
</Emails>
");
var email = xmlNavigation.Elements("Email");
In the above example, to fetch all the "Email" elements, we use the Elements ("Emails")
statement. In the same way, we use other methods to manipulate other XML reading operations. Here, our work has been done for fetching elements, but it does not come in easily readable format. We can achieve that kind of functionality using the DynamicObject
class and a dynamic
keyword, which allows us to navigate through the XML in easily readable format. We just need to keep track of XmlStructure
.
Let's talk a bit about the DynamicObject
class.
The DynamicObject
class allows us to implement operations dynamically, when we try to get member values or set member values. Here we will look at the TryGetMember
method of the DynamicObject
class which will be called when we will try to get the values of properties which are not implemented in the class.
Let us see an example of XML navigation using the DynamincObject
class and the dynamic
keyword.
namespace XmlNavigationUsingDynamic
{
class Program
{
static void Main(string[] args)
{
dynamic xmlNavigation = new XmlNavigationUsingDynamic
(XElement.Parse(@"
<Emails>
<Email>
<Id>1</Id>
<Description>sanjay.patolia@gmail.com</Description>
</Email>
<Email>
<Id>2</Id>
<Description>sanjay.patolia@patni.com</Description>
</Email>
</Emails>
"));
IEnumerable<XElement> descriptionNodes = xmlNavigation.Email.Description;
descriptionNodes.ToList().ForEach(emailDesc =>
{
Console.WriteLine(emailDesc);
});
Console.ReadKey(true);
}
}
public class XmlNavigationUsingDynamic : DynamicObject, IEnumerable<XElement>
{
IEnumerable<XElement> node = null;
public XmlNavigationUsingDynamic(params XElement[] nodes)
{
node = nodes;
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
var genericNode = node.Elements(binder.Name).ToArray();
result = new XmlNavigationUsingDynamic(genericNode);
return true;
}
public IEnumerator<XElement> GetEnumerator()
{
return node.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
}
In the above example:
dynamic xmlNavigation = new XmlNavigationUsingDynamic
(XElement.Parse(@"
<Emails>
<Email>
<Id>1</Id>
<Description>sanjay.patolia@gmail.com</Description>
</Email>
<Email>
<Id>2</Id>
<Description>sanjay.patolia@patni.com</Description>
</Email>
</Emails>
"));
creates an XML file in memory.
xmlNavigation.Email
xmlNavigation
is an object of the XmlNavigationUsingDynamic
class and Email
is a member of that class. We are trying to access a member of that class. This statement will call the TryGetMember
method; it performs an operation and sets the result object which will be the base of "Description
", because the result object is set to an object of the XmlNavigationUsingDynamic
class.
xmlNavigation.Email.Description
Description
will be called from the result (object set from the TryGetMember
method) and returns IEnumerable<XElement>
because we have implemented IEnumerable<XElement>
, and the node.GetEnumerator()
method is called which is implemented from the IEnumerable
interface and returns IEnumerable<XElement>
and we iterate it to get the element "Description
".
Here we can see the difference in traversing the XML file:
var email = xmlNavigation.Elements("Email");
IEnumerable<XElement> descriptionNodes = xmlNavigation.Email.Description;
In this way we can perform other XML navigation operations too.
Performance
Use of dynamic
in XML navigation or manipulating XML in other ways provides easy readability and simplicity but will have a bit of performance hit.
4. Reflection using dynamic
Introduction
Wherever we find .GetProperties () or .GetType () in code, we can go for dynamic with a lower cost in performance.
Reflection is all about manipulating data based on metadata. Reflection provides objects that wrap assemblies, modules, and types. We can use Reflection to dynamically create an instance of a type, bind the type to an existing object, and invoke its methods or access its fields and properties.
Let’s take a tiny example of Reflection.
int i = 4;
System.Type type = i.GetType ();
Console.WriteLine (type);
Output is: System.Int32.
Let’s take another example of Reflection:
System.Reflection.Assembly obj =
System.Reflection.Assembly.Load ("Sample.dll");
System.Console.WriteLine (obj.GetName ());
In the same way, we can create an instance at run time based on a specified type. And then we can call the methods, and access properties of that type using the instance object.
For example:
Assembly obj = System.Reflection.Assembly.Load("Sample.dll");
obj.GetTypes().ToList().ForEach(type =>
{
object objInstance = Activator.CreateInstance(type);
MethodInfo methodInfo = type.GetMethod("Display");
methodInfo.Invoke(objInstance, null);
});
In the above example, let’s assume that the Sample.dll assembly has only one class named “Sample
” and only one method in it named “Display
”. We have loaded the assembly dynamically, iterated all the types under that assembly, created an instance of all the types using Activator, and tried to call a member of a type where the member can be a property or a method.
In the above example, we use the MethodInfo
class to get a method name and then call the method using the Invoke
method of the MethodInfo
class, providing the object instance and parameters in the argument of the Invoke
method to call the method Display
.
The same behavior with a lower cost can be achieved using dynamic
.
The first statement in the lambda expression creates an instance of object
type and we can not call any member of the object
type because we don’t know the type here. Why don’t we set that instance type as a dynamic type so that we can call its members at run time?
For example:
Assembly obj = System.Reflection.Assembly.Load("Sample.dll");
obj.GetTypes().ToList().ForEach(type =>
{
dynamic objInstance = Activator.CreateInstance(type);
objInstance.Display();
});
Now, we compare the above two examples, with dynamic
and without dynamic
. We will find a major difference in code because we are not using MethodInfo
and all. This way, we can filter other classes used in Reflection too.
We can call any member of a dynamic type at run time. We will call a method directly without needing any further logic, such as MethoInfo
. It is a pretty simple and cost saving concept to use dynamic
in place of Reflection.
Performance
Use of dynamic
in Reflection achieves better performance in comparison of typical Reflection techniques.