Working with Dynamic Languages/Objects

Most .NET development is about working with strongly typed objects where the compiler knows in advance the properties and methods that a given class exposes. However, there are objects (and languages) out there that do not have a static structure against which you can program. Instead, they are designed to get their information at runtime based on data inside an HTML form, a text file, XML, a database, or something similar. These objects and languages are said to be dynamic in that they get their structure only at runtime. Dynamic support was added to .NET for the purpose of simplifying the access to dynamic application programming interfaces (APIs) provided by languages such as IronPython and IronRuby or even those found in Office Automation.

The Dynamic Data Type

The C# language has a new data type called dynamic. This type is similar to object in that it might contain any actual type. In fact, in Visual Basic you simply use object to get dynamic-like behavior. The difference in C#, however, is that any value defined as dynamic only has its actual type inferred at runtime (and not at compile time). This means you do not have type checking against valid methods. That is, the compiler does not stop you from writing code against methods or properties it cannot see at design time. Instead, type checking is only done when the code executes. Of course, this means that your dynamic type should be the right type at the right time or you get errors.

You can define dynamic fields, properties, variable, or return types of methods. For example, the following shows a property defined as a dynamic.

public dynamic DyProperty { get; set; }

At first glance, it would seem that the dynamic keyword simply makes the type behave like types declared as objects. In fact, the differences are so slight that Visual Basic combines the concept of object and dynamic. However, in C#, the keyword dynamic indicates that the property can contain any value and that no type checking is done at compile time regardless of what the code looks like that uses the property. That is in contrast to types declared as objects, in which the compiler evaluates expressions that use the type and prevents certain code (such as doing arithmetic with objects). Dynamic types do not get this scrutiny by the compiler and therefore either execute properly or throw an error if a problem exists.

Dynamics are useful for dealing with types and code outside of .NET, such as IronPython. However, you have to be careful when using them for your own needs. Because no resolution is done until runtime, you do not get strong type checking by the compiler or with IntelliSense. There is also a slight memory and performance penalty to pay at runtime for dynamic objects.

Figure 3.4 shows an example of the experience inside Visual Studio. There are two methods here. The first, VarTestMethod, uses the var statement to create an instance of the Employee class. Notice an attempt to call the nonexistent property NewName is type-checked as an error. The second method, DynTestMethod, declares a dynamic instance of Employee. In this case, a call to emp.NewName does not get a compile time error. The compiler allows this call, but an exception will be thrown at runtime if you get it wrong.

Image

FIGURE 3.4 Using dynamics means no type checking even in IntelliSense.

Creating a Custom Dynamic Object

A dynamic object is one that gets its type information for things such as properties and methods at runtime. This is typically due to the fact that the object is meant to represent dynamic information such as that contained in an HTML or XML script file. In both cases, the underlying HTML and XML files you create are unique to your needs. Therefore, you cannot code directly against these models. Instead, you often have to code against static objects and write syntax such as MyXml.GetElement("EmployeeId"). In this example, the GetElement method then searches for the given XML element and returns the same. With a dynamic object, the object can be written to interrogate your XML (or similar data) and enables developers to code against the dynamic object as if it contained the EmployeeId property. For example, developers could use your dynamic object to write their code as MyXml.EmployeeId. The dynamic object still has to interrogate the underlying structure for an EmployeeId, but this does simplify the coding for those working with your object and a dynamic structure such as XML or HTML.

You can create dynamic objects using either Visual Basic or C#. To do so, you inherit from the DynamicObject class inside the System.Dynamic namespace. You then override the members inside this class. These members serve as the basis for your dynamic items. For example, you can override the TrySetMember and TryGetMember to indicate the code that should be run when a user attempts to set or get a dynamic property on your object (such as calling MyXml.EmployeeId). In this case, if a user is trying to return a dynamic property, the TryGetMember method is called. Your code then determines how to return information for the dynamic property. (You might interrogate a file, for instance.)

There are many members on DynamicObject for which you can provide functionality. In addition to the two aforementioned members, the other notables include TryInvokeMember for invoking dynamic methods and TryCreateInstance for creating new instances of a dynamic object.

You might also add your own methods and properties to a dynamic object. In this case, the dynamic object first looks for your property or method before calling out to the appropriate Try member.

Let’s look at an example. Suppose that you were to write a dynamic object to represent an Employee. In this case, perhaps you get data scraped from a web page or inside an XML file. You therefore want to convert this data to an object for easier programming. In this case, you can create a new class called Employee and make sure it inherits from DynamicObject. In our example, we use a simple Hashtable of key value pairs to simulate the employee data. When a user creates an instance of this class, he is expected to pass the employee data to the dynamic class in the constructor. The skeleton of this class might then look like this.

C#

class Employee : System.Dynamic.DynamicObject
{
  Hashtable _memberData;

  public Employee(Hashtable employeeData)
  {
    _memberData = employeeData;
  }
}

VB

Public Class Employee
  Inherits System.Dynamic.DynamicObject

  Dim _memberData As Hashtable

  Public Sub New(ByVal employeeData As Hashtable)
    _memberData = employeeData
  End Sub

End Class

The next step is to override one or more of the Try members of DynamicObject to add our own functionality. In this simple example, we override the TryGetMember method to provide functionality for reading a property and add it to the Employee class created earlier. This method takes two parameters: binder and result. The binder parameter is an object that represents the dynamic call made to your object (such as its name). The result parameter is an outbound parameter of type object. You use it to pass back any value you intend to pass as the property read. Finally, the method returns a bool. This indicates true if the member was determined to exist; otherwise, you return false.

In the example, we simply look inside the Hashtable for a given key (based on the binder.Name property). If it exists, we set the result to its value and return true. Otherwise, we set the result to null and return false. The following shows the code for this additional member of our Employee class (assumes you’re using [imports in VB] System.Dynamic).

C#

public override bool TryGetMember(
  GetMemberBinder binder, out object result)
{
  if (_memberData.ContainsKey(binder.Name))
  {
    //set the out parameter results to the value in the
    //  hash table for the given key
    result = _memberData[binder.Name];

    //indicate that member existed
    return true;
  }
  else
  {
    //property does not exist in hash table
    result = null;
    return false;
  }
}

VB

Public Overrides Function TryGetMember(ByVal binder As GetMemberBinder,
  ByRef result As Object) As Boolean

  If _memberData.ContainsKey(binder.Name) Then
    'set the out parameter results to the value in the
    '  hash table for the given key
    result = _memberData(binder.Name)

    'indicate that member existed
    Return True
  Else
    'property does not exist in hash table
    result = Nothing
    Return False
  End If

End Function


Note

Note that classes that inherit from DynamicObject can be passed as instances to other languages that support the dynamic interoperability model. This includes IronPython and IronRuby.


Using the Dynamic Object

You use a dynamic object like you would any other. You can create an instance, call methods and properties, and so on. However, you do not get type checking by the compiler. Again, this is because the object is late bound at runtime. In C#, you indicate a late-bound dynamic object using the keyword dynamic. In Visual Basic, you simply declare your type as object. Visual Basic figures out whether you are using late binding.

For example, suppose that you want to use the dynamic version of the Employee class created in the previous section. Recall that this class simulates converting data into an object. In this case, the simulation is handled through a Hashtable. Therefore, you need to declare an instance of the Employee class as dynamic (or object in VB) and then create an instance passing in a valid Hashtable. You can then call late-bound properties against your object. Recall that these properties are evaluated inside the TryGetMember method you overrode in the previous example. The following shows a Console application that calls the dynamic Employee object.

C#

class Program
{
  static void Main(string[] args)
  {
    Hashtable empData = new Hashtable();
    empData.Add("Name", "Dave Elper");
    empData.Add("Salary", 75000);
    empData.Add("Title", "Developer");

    dynamic dyEmp = new Employee(empData);

    Console.WriteLine(dyEmp.Name);
    Console.WriteLine(dyEmp.Salary);
    Console.WriteLine(dyEmp.Title);
    Console.WriteLine(dyEmp.Status);

    Console.ReadLine();
  }
}

VB

Module Module1

  Sub Main()

    Dim empData As New Hashtable()
    empData.Add("Name", "Dave Elper")
    empData.Add("Salary", 75000)
    empData.Add("Title", "Developer")

    Dim dyEmp As Object = New Employee(empData)

    Console.WriteLine(dyEmp.Name)
    Console.WriteLine(dyEmp.Salary)
    Console.WriteLine(dyEmp.Title)
    Console.WriteLine(dyEmp.Status)

    Console.ReadLine()

  End Sub

End Module

All this code passes the compiler’s test and executes accordingly. However, the last call to dyEmp.Status is not valid. In this case, the dynamic object returns false and thus throws an error. Figure 3.5 shows the results, including the Console output and the error message trying to access a bad member.

Image

FIGURE 3.5 The dynamic object executing in the Console and throwing an error in Visual Studio.


Tip

You can use the features discussed here to load a dynamic language library such as IronPython. In this case, you load the dynamic language library and can then use this library inside your code. For more on this, see “Creating and Using Dynamic Objects (C# and Visual Basic)” inside MSDN.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset