Appendix E

Dynamic Types and Languages

WROX.COM CODE DOWNLOADS FOR THIS APPENDIX

Please note that all the code examples in this appendix are available as a part of this appendix’s code download on the book’s website at www.wrox.com on the Download Code tab.

When Microsoft originally introduced .NET, many developers, especially those coming from Visual Basic 6 (or earlier), were brought into a new world of statically typed languages. No longer were developers allowed to create variant types, or easily create instances of objects at run time using late binding. The new .NET-based languages forced them to define all of their objects using a well-known type, and if the compiler did not recognize that type, it would throw errors back at them.

Purists out there will argue that .NET does indeed support late binding, and it is true that VB.NET can accommodate late binding through disabling the Option Strict command, though this is certainly discouraged in most situations. In C# if you want to simulate late binding you have to resort to reflection, which usually means a lot more lines of code and application complexity.

Although statically typed languages have many advantages, such as strict type safety and the ability to leverage the compiler to optimize based on well-known type information, you do lose a bit of flexibility that you get from a dynamically typed language. Additionally, the lack of dynamism does make interoperating between .NET and dynamic languages more difficult. You may have experienced this if you have ever had to create applications that require COM interop.

This appendix looks at some of the work Microsoft has done to embrace the concepts of dynamic languages and how it continues to make working directly with, as well as interoperating with, dynamic languages easier for developers.

IMPLICIT TYPES

Since implicit types were introduced in the .NET Framework, they have added flexibility to the way variables can be declared. Implicit types are expressed in C# using the var keyword or in VB.NET by declaring a variable without the As operator. They enable you to declare a variable whose type is implicitly inferred from the expression used to initialize the variable. In other words, implicit types enable you to declare variables in a fairly dynamic way in your code, while through a bit of compiler magic, retaining the benefits of a statically typed variable at run time. An example of using implicit types is shown here:

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim foo = 1
End Sub

C#

protected void Page_Load(object sender, EventArgs e)
{
    var foo = 1;
}

In this sample, a variable foo is declared as an implicit type, and immediately assigned a numeric value of 1. Because the variable is assigned a numeric, the compiler will automatically infer that the variable foo should actually be of type Int32. In fact if you decompile the code to its Intermediate Language (IL), you will see that the code actually output by the compiler emits the variable as the correct type. Trying to assign a different type to this variable as shown in the following results in a type mismatch exception:

VB

Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
    Dim foo = 1
    foo = "abc"
End Sub

C#

protected void Page_Load(object sender, EventArgs e)
{
    var foo = 1;
    foo = "abc";
}

Trying to compile this code results in a compiler error letting you know that a string cannot be assigned to the foo variable.

THE DYNAMIC LANGUAGE RUNTIME

To help developers gain back some of that flexibility, as well as bring the general goodness that the .NET Framework offers to a wider audience of developers, in 2007 Microsoft announced it had begun working on a new initiative called the dynamic language runtime, or DLR. With the release of .NET 4.0, the Common Language Runtime (CLR) was supplemented with the DLR. It brings to the CLR a dynamic type system, dynamic method dispatch, dynamic code generation, and a hosting API. The DLR enables dynamic languages to run with the .NET run time. Using the DLR, these dynamic languages get the best of both environments. They run in a dynamic environment in which types are discovered at run time and they have access to the richer .NET Base Class Libraries. IronPython and IronRuby, ports of two popular dynamic languages, currently take advantage of the DLR.

Both the IronRuby and IronPython languages were incubated from within Microsoft and released under open source licenses. The IronRuby and IronPython projects began as an effort for Microsoft to improve support for dynamic languages in the .NET Framework and to diversify its portfolio of programming languages. As of the last quarter of 2010, Microsoft turned the development of these Iron projects over to the community. Other dynamic languages are developed and maintained by third-party developers as well.


NOTE You can find more information on IronPython, the DLR implementation of Python, at www.ironpython.net.
You can find more information on IronRuby, the DLR implementation of Ruby, at www.ironruby.net.

Because the development of DLR-based languages was separate from the CLR-based languages, and support for the DLR-based languages has now been turned over to the community, support for these languages inside of Visual Studio is not very rich. Tools are available for both IronPython and IronRuby that integrate with Visual Studio, but the features vary to a large degree.

After you’ve loaded the languages on your system, you can develop standalone applications using them or leverage language libraries from other static languages like C# or VB.NET. Listing E-1 (IronPythonList.aspx in the IronPythonCS and IronPythonVB projects in the code download for this appendix) demonstrates using IronPython code from within an ASP.NET application using both C# and VB.NET. Note that to execute the example code, you will need to install version 2.7 of IronPython and reference both the Microsoft.Scripting.dll and IronPython.dll assemblies located in the default install location \Program Files (x86)\IronPython 2.7\Platforms\Net40.

LISTING E-1: Calling IronPython libraries from ASP.NET

VB

<%@ Page Language="VB" %>
          
<!DOCTYPE html>
 
<script runat="server">
    Dim items() As Integer = Enumerable.Range(1, 5).ToArray()
    Dim subitems() As Integer = Enumerable.Range(1, 7).ToArray()
          
    Dim random As Object
          
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs)
        System.IO.Directory.SetCurrentDirectory( _
            Environment.GetFolderPath( _
                Environment.SpecialFolder.ProgramFiles) & "\IronPython 2.7\Lib")
          
        Dim py As Microsoft.Scripting.Hosting.ScriptRuntime = _
            IronPython.Hosting.Python.CreateRuntime()
          
        random = py.UseFile("random.py")
          
        Me.Repeater1.DataSource = items
        Me.Repeater1.DataBind()
          
    End Sub
          
    Protected Sub Repeater1_ItemDataBound(ByVal sender As Object,
        ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs)
          
        Dim lbl As Label = CType(e.Item.FindControl("Label"), Label)
        lbl.Text = String.Format("List Number: {0}", e.Item.ItemIndex)
          
        Dim list As BulletedList = CType(e.Item.FindControl("BulletedList"),  _
            BulletedList)
          
        random.shuffle(subitems)
          
        list.DataSource = subitems
        list.DataBind()
    End Sub
</script>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Repeater ID="Repeater1" runat="server" 
                OnItemDataBound="Repeater1_ItemDataBound">
                <ItemTemplate>
                    <p>
                        <asp:Label runat="server" ID="Label" />
                        <br />
                        <asp:BulletedList runat="server" ID="BulletedList" />
                    </p>
                </ItemTemplate>
            </asp:Repeater>
        </div>
    </form>
</body>
</html>

C#

<%@ Page Language="C#"%>
 
<!DOCTYPE html>
 
<script runat="server">
int[] items = Enumerable.Range(1, 5).ToArray();
int[] subitems = Enumerable.Range(1, 7).ToArray();
dynamic random;
 
protected void Page_Load(object sender, EventArgs e)
{
    System.IO.Directory.SetCurrentDirectory(
        Environment.GetFolderPath(
            Environment.SpecialFolder.ProgramFiles) + @"\IronPython 2.7\Lib");
 
    Microsoft.Scripting.Hosting.ScriptRuntime py =
        IronPython.Hosting.Python.CreateRuntime();
    random = py.UseFile("random.py");
          
    this.Repeater1.DataSource = items;
    this.Repeater1.DataBind();
}
          
protected void Repeater1_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
    Label lbl = (Label)e.Item.FindControl("Label");
    lbl.Text = string.Format("List Number: {0}", e.Item.ItemIndex);
    
    BulletedList list = (BulletedList)e.Item.FindControl("BulletedList");
    random.shuffle(subitems);
    list.DataSource = subitems;
    list.DataBind();
}
</script>
 
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
        <div>
            <asp:Repeater ID="Repeater1" runat="server" 
                OnItemDataBound="Repeater1_ItemDataBound">
                <ItemTemplate>
                    <p>
                        <asp:Label runat="server" ID="Label" />
                        <br />
                        <asp:BulletedList runat="server" ID="BulletedList" />
                    </p>
                </ItemTemplate>
            </asp:Repeater>
        </div>
    </form>
</body>
</html>

You can see in this listing that when the page is loaded, the IronPython library random.py is loaded from the IronPython install directory using the DLR’s hosting API. As each item in the items array is bound to the Repeater, its subitems are shuffled into a random order using the IronPython library.

DYNAMIC LOOKUP

In C# 4, a new feature was introduced called dynamic lookup. Although .NET developers have long been familiar with late binding using reflection, dynamic lookup brings a truly dynamic type declaration mechanism to C#. Dynamic lookup enables you to explicitly declare a variable as a dynamic type in your code and dynamically invoke methods against that type at run time. This differs from implicit typing in that dynamic types remain truly dynamic even at run time, whereas implicit types are converted to static types at compile-time. The following code shows a simple example of using the new dynamic keyword:

<script runat="server">
    protected void Page_Load(object sender, EventArgs e)
    {
        dynamic dynvalue =
            "Even though assigned a string, the type is still dynamic";
    }
</script>

In this sample, the property dynvalue is assigned a simple string as a value; however, unlike with implicit types, the variable remains typed as a dynamic type even at run time. You can see this dynamism if you are trying to access any member of the variable in Visual Studio. Normally, Visual Studio would show you a list of available properties and methods as well as the type of the variable. But because this type is dynamic, none of this information is known until run time. You can see this in Visual Studio by observing the tooltip on a dynamic variable as is shown in Figure E-1.

FIGURE E-1

image

At run time, the dynamic language runtime’s dynamic dispatch system uses dynamic invocation to execute methods and properties of the type. This means that you can add and remove members from a type at run time. .NET provides two mechanisms to do this: the ExpandoObject class and the DynamicObject class.

You can use the ExpandoObject class in relatively simple scenarios where you need to add or remove members dynamically. The following code demonstrates using the ExpandoObject:

dynamic contact = new System.Dynamic.ExpandoObject();
contact.Name = "John Doe";
contact.Phone = "201-555-5555";
contact.Address = new System.Dynamic.ExpandoObject();
contact.Address.Street = "123 Main St";
contact.Address.City = "Anywhere";
contact.Address.State = "WA";
contact.Address.Postal = "12345";

In this sample code you can see that an ExpandoObject is created and several properties are added. These properties are added dynamically at run time and stored internally as an IDictionary<String,Object>, which the ExpandoObject implements internally to maintain the list of members.

An ExpandoObject is sealed and cannot be further inherited. If you need more control over what specific operations can be performed on a dynamic object, or what happens when an operation like getting and setting properties or calling a method occurs, you can create objects that derive from DynamicObject. An example of deriving from the DynamicObject class is shown using the JsonObject class (DynamicRest/JsonObject.cs in the DynamicRest project in the code download for this appendix). Part of this class is shown in the following code:

namespace DynamicRest {
          
    public sealed class JsonObject :
                                      DynamicObject,
                                      IDictionary<string, object>,
                                      IDictionary
{
          
        private Dictionary<string, object> _members;
          
        public JsonObject()
        {
            _members = new Dictionary<string, object>();
        }
          
        public JsonObject(params object[] nameValuePairs) : this() {
            if (nameValuePairs != null) {
                if (nameValuePairs.Length % 2 != 0) {
                    throw new ArgumentException(
                        "Mismatch in name/value pairs.");
                }
          
                for (int i = 0; i < nameValuePairs.Length; i += 2) {
                    if (!(nameValuePairs[i] is string)) {
                        throw new ArgumentException(
                            "Name parameters must be strings.");
                    }
          
                    _members[(string)nameValuePairs[i]] =
                         nameValuePairs[i + 1];
                }
            }
        }
          
        public override bool TryConvert(ConvertBinder binder,
                                        out object result)
        {
            Type targetType = binder.ReturnType;
          
            if ((targetType == typeof(IEnumerable)) ||
                (targetType ==
                    typeof(IEnumerable<KeyValuePair<string, object>>)) ||
                (targetType == typeof(IDictionary<string, object>)) ||
                (targetType == typeof(IDictionary)))
            {
                result = this;
                return true;
            }
          
            return base.TryConvert(binder, out result);
        }
          
        public override bool TryDeleteMember(DeleteMemberBinder binder)
        {
            return _members.Remove(binder.Name);
        }
          
        public override bool TryGetMember(GetMemberBinder binder,
                                          out object result)
        {
            object value;
            if (_members.TryGetValue(binder.Name, out value))
            {
                result = value;
                return true;
            }
          
            return base.TryGetMember(binder, out result);
        }
          
        public override bool TrySetMember(SetMemberBinder binder,
                                          object value)
        {
            _members[binder.Name] = value;
            return true;
        }
          
        // ++++
        // Non-related interface implementations removed for clarity
    }
}

This JsonObject class is part of a larger library based on a sample written by Nikhil Kothari that simplifies retrieving and parsing JSON-formatted data. In .NET 4.5, the System.Json namespace was added, which provides the ability to more easily deal with JSON data. However, prior to that, to deal with JSON data in .NET, you would have to create proxy types that mirror the structure of the JSON and then perform complex parsing operations to parse the JSON data into collections of the custom types. Using the dynamic capabilities originally introduced in C# 4, you could simplify this by parsing the JSON data into generic types that expose the data via dynamic properties and methods, which are inferred at run time. Thus, the JsonObject class is a good example of the use of the DynamicObject. As you can see from the code example, you can override methods like TryGetMember and TrySetMember to control how to get and set properties on the type. In this case, members are stored in an internal Dictionary object in the JsonObject class.

The teams within Microsoft also leveraged the dynamic capabilities found in .NET 4.5 to make COM interop operations easier. The Office Primary Interop Assemblies (PIAs), which provide a managed layer over the Office COM Automation APIs, have been updated to leverage the dynamic capabilities of C#. The following code sample (ExcelInterop.vb and ExcelInterop.cs in the code download for this appendix) demonstrates how to interact with Excel from .NET using the PIAs:

VB

  Dim excelApp As New Microsoft.Office.Interop.Excel.Application()
  excelApp.Visible = True
  excelApp.Workbooks.Add()
          
  Dim workSheet As Microsoft.Office.Interop.Excel.Worksheet =
    excelApp.ActiveSheet
  workSheet.Cells(1, "A") = "ID Number"
  workSheet.Cells(1, "B") = "Current Balance"
          
  Dim row = 1
          
  For Each acct In accounts
    row = row + 1
    workSheet.Cells(row, "A") = acct.ID
    workSheet.Cells(row, "B") = acct.Balance
  Next
          
  workSheet.Columns(1).AutoFit()
  workSheet.Columns(2).AutoFit()

C#

  var excelApp = new Microsoft.Office.Interop.Excel.Application();
  excelApp.Visible = true;
  excelApp.Workbooks.Add();
          
  Microsoft.Office.Interop.Excel.Worksheet workSheet = excelApp.ActiveSheet;
  workSheet.Cells[1, "A"] = "ID Number";
  workSheet.Cells[1, "B"] = "Current Balance";
          
  var row = 1;
  foreach (var acct in accounts)
  {
    row++;
    workSheet.Cells[row, "A"] = acct.ID;
    workSheet.Cells[row, "B"] = acct.Balance;
  }
          
  workSheet.Columns[1].AutoFit();
  workSheet.Columns[2].AutoFit();

In prior versions of the PIAs, accessing certain APIs such as the Columns collection shown in the sample would have required you to cast the objects returned to the appropriate type to access that type’s properties and methods. The newer versions, however, leverage the dynamic keyword to remove this requirement and allow for dynamic lookup of those properties and methods.

SUMMARY

Microsoft continues to expand the functionality of the .NET Framework and its languages by investing in features to bring more dynamism to the languages. These features give you additional programming tools in your tool belt, enabling you to leverage the features, or even the programming languages, that make the most sense for your specific application. From built-in features of C# and VB.NET such as implicit types and dynamic lookup, to entirely new languages such as IronPython and IronRuby, the choices available to you continue to expand.