Introduction
This article will show you how to simulate dynamic binding in C#.
Background
To understand the meaning of dynamic binding, one can look at SRFI 39 which is an implementation of a dynamic binding mechanism for the R5RS Scheme. Dynamic binding is also frequently used in LISP. Another commonly used dynamic binding is the 'this
' keyword in JavaScript. For an in-depth explanation, please read chapter 2 of LISP in Small Pieces by Christian Queinnec.
To understand what this code does (in terms of dynamic binding), consider the following pseudo C# snippet:
var orig-value = getvalue(location);
setvalue(location, new-value);
try
{
body
}
finally
{
setvalue(location, orig-value);
}
Firstly, it gets the original value, which is stored. Then it sets the value at the location (property or field or a getter/setter method pair) to the new value. Once the execution leaves the body part, the original value is restored unconditionally. This means even in the presence of exceptions, the value is faithfully restored.
Another aspect is the fact that dynamic bindings can be nested within one another. For example, we can place the same construct within the body part.
This illustrates the essence of dynamic binding, and how it is implemented in the code complementing this article.
Using the Code
The code is in the form of Extension Methods that are encapsulated in a single class. To use the code, you can copy the class anywhere and simply include the containing namespace.
The 'API' exposes two methods with two overloads each.
The Bind
method does a plain dynamic binding, while the With
method allows the dynamic binding to be disposed automatically when exiting the body of the dynamic binding. Both methods should be called in the expression part of the using
construct in C#.
Use the following as a guide to the terminology used above and throughout the article:
using (expression)
{
body
}
Each of the above methods provides an additional overloaded method taking an Expression<Func<T>>
argument for convenience. This allows one to build getter and setter delegates for both properties and fields.
Here follows the method signatures and documentation for the exposed methods:
public static IDisposable Bind<T>(this T newvalue, Func<T> getter, Action<T> setter)
public static IDisposable Bind<T>(this T newvalue, Expression<Func<T>> locator)
public static IDisposable With<T>(this T newvalue, Func<T> getter,
Action<T> setter) where T : IDisposable
public static IDisposable With<T>(this T newvalue,
Expression<Func<T>> locator) where T : IDisposable
Usage Examples
The attached code contains several examples to illustrate the usage of this dynamic binding implementation. In each of the examples, the original value will be restored after exiting the using
construct's body.
The following class declaration will be used in some of the examples:
class TestClass
{
public string Test { get; set; }
public static object StaticField;
}
Instance Property Example
var tc = new TestClass { Test = "Foo" };
Console.WriteLine(tc.Test);
using ("Bar".Bind(() => tc.Test))
{
Console.WriteLine(tc.Test);
using ("Baz".Bind(() => tc.Test))
{
Console.WriteLine(tc.Test);
}
Console.WriteLine(tc.Test);
}
Console.WriteLine(tc.Test);
Static Property Example
Console.WriteLine(Console.ForegroundColor);
using (ConsoleColor.Red.Bind(() => Console.ForegroundColor))
{
Console.WriteLine(Console.ForegroundColor);
}
Console.WriteLine(Console.ForegroundColor);
Local Variable Example
var i = 0;
Console.WriteLine(i);
using (10.Bind(() => i))
{
Console.WriteLine(i);
}
Console.WriteLine(i);
Static Field Example
Console.WriteLine(TestClass.StaticField);
using (10.Bind(() => TestClass.StaticField))
{
Console.WriteLine(TestClass.StaticField);
}
Console.WriteLine(TestClass.StaticField);
Getter and Setter Method Pair Example with Sample Extension
This example makes use of a helper extension method called AsConsoleOutput()
:
public static IDisposable AsConsoleOutput<T>(this T writer) where T : TextWriter
{
return writer.With(() => Console.Out, Console.SetOut);
}
Usage is then:
Console.WriteLine("Console");
using (File.CreateText("warning.txt").AsConsoleOutput())
{
Console.WriteLine("Warning");
using (File.CreateText("error.txt").AsConsoleOutput())
{
Console.WriteLine("Error");
}
Console.WriteLine("Warning");
}
Console.WriteLine("Console");
Points of Interest
The implementation makes heavy use of LINQ Expressions, and DynamicMethod
from System.Reflection.Emit
for performance reasons.
The implementation is only 152 lines long, including the XML documentation comments, liberal whitespace, and error checking.
Incidentally, Tomas Petricek of F# fame just posted an implementation of this in F# today after I 'announced' my intentions. The snippet can be viewed @ http://www.fssnip.net/2s. Please note that I blatantly stole his ConsoleColor
example :).
History
- 4th February, 2011: Initial version