Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Introducing the LinFu Framework, Part II: LinFu.DynamicObject – Adding Dynamic Language Features to Statically Typed Languages

0.00/5 (No votes)
12 Nov 2007 2  
Using LinFu.DynamicObject to add mixins, duck typing and multiple dispatch to your favorite .NET languages
Screenshot - Duck.jpg

Introduction

In this installment of the series, I'll show you how you can use LinFu.DynamicObject to add dynamic language features to your code. I'll also discuss how LinFu.DynamicObject does this behind the scenes and briefly touch upon how you can use it in conjunction with the new language features coming up in C# 3.0. Note: If you're wondering what the entire LinFu Framework is about, click here.

Background

Since the version 1.0 release of the .NET Framework, many developers have been clamoring for features such as multiple inheritance and mixins. Unfortunately, due to the complexity of implementing multiple inheritance, mixins and the trouble those features entail, Microsoft left those features out of the .NET Framework.

Until recently, these feature requests have been largely ignored. However, thanks to upcoming dynamic languages like IronPython and IronRuby, Microsoft is finally starting to provide those language features that everyone else has been asking for. However, in order to use those features in your application, you have to switch your codebase to a new programming language. That might not be a feasible option if one has deadlines to meet and milestones to reach.

This scenario effectively leaves us developers with two options: One can either find a workaround for the statically typed, single-inheritance limitations imposed by C#, VB.NET and the CLR, or one can take Reflection.Emit into their own hands and implement those features themselves. Most sane developers would have taken the first option. Thankfully, I am not a sane person when it comes to coding and thus, LinFu.DynamicObject was born.

What You Need to Know

This article assumes that you know enough about dynamic language features such as mixins, multiple dispatch and the like. It also assumes that you understand enough about dynamic proxies to make use of the library.

Features and Usage

One of the most notable features about LinFu.DynamicObject and the rest of the LinFu library is that it is language-independent. Although the examples shown here are done in C#, anyone can easily use it in the same way in other languages, such as VB.NET. Aside from whichever programming language you wish to use, LinFu.DynamicObject supports the following features:

Late Binding

Unlike typical method calls made by an application, LinFu.DynamicObject doesn't know which method you will be using until the application is running. What LinFu.DynamicObject does is actually determine which method you'll be using just by looking at the name of the method that you're calling, and the runtime arguments that are being passed to that method.

Here's an example. Suppose that I had an instance of a System.IO.StringWriter class, which has multiple overloads of the WriteLine() method:

StringWriter writer = new StringWriter();
DynamicObject dynamic = new DynamicObject(writer);

// Note that there are 18 overloads for TextWriter.WriteLine()

// Call WriteLine(string text);
dynamic.Methods["WriteLine"]("Hello, World!");

// Call Writeline(bool value);
dynamic.Methods["WriteLine"](false);

No matter what the method signature, LinFu.DynamicObject makes it easy to perform late-bound calls to objects at runtime.

Multiple Dispatch

As I mentioned earlier, LinFu.DynamicObject can match any method signature with any arbitrary number of arguments. Using this feature, I can do things that would otherwise be difficult (or impossible) to implement just by using the native features of a statically typed language alone. For example, suppose that I had a class named CollisionManager that managed a bunch of shapes on the screen and that I wanted it to perform a certain set of actions when a certain set of shapes Collide():

public class CollisionManager
{
   // Overload #1
   public void Collide(Circle circle, Square square, Triangle triangle)
   {
      Console.WriteLine("Overload #1 Called");
   }

   // Overload #2
   public void Collide(Square square, Circle circle, Triangle triangle)
   {
      Console.WriteLine("Overload #2 Called");
   }

   // Overload #3
   public void Collide(Triangle triangle, Square square, Circle circle)
   {
      Console.WriteLine("Overload #3 Called");
   }

   // Overload #4
   public void Collide(Triangle triangle, Circle circle)
   {
      Console.WriteLine("Overload #4 Called");
   }
}

A Collision with Insanity

Now, suppose that I wanted to be able to call any one of these overloads, depending on both the order and type of each parameter, using only a single method call. Needless to say, doing something like this in a statically typed language is an outright nightmare. Fortunately, with LinFu.DynamicObject, you no longer have to do this by hand. Calling those overloaded methods is just as simple as:

// Wrap the CollisionManager instance
DynamicObject dynamic = new DynamicObject(new CollisionManager());

object[] list1 = new object[]{new Circle(), new Square(), new Triangle()};
object[] list2 = new object[]{new Square(), new Circle(), new Triangle()};
object[] list3 = new object[]{new Triangle(), new Square(), new Circle()};
object[] list4 = new object[]{new Circle(), new Triangle()};

// Call Overload #1
dynamic.Methods["Collide"](list1);

// Call Overload #2
dynamic.Methods["Collide"](list2);

// Call Overload #3
dynamic.Methods["Collide"](list3);

// Call Overload #4
dynamic.Methods["Collide"](list4);

As you can see from the example above, LinFu.DynamicObject makes it easy to call the appropriate Collide() method in spite of the number of overloads that might exist for that particular method. It can easily distinguish between the different method signatures. It does all of the boilerplate work for the developer, allowing you to focus on the more important tasks at hand.

Dynamic Methods

DynamicObject also allows you to dynamically add methods to it at runtime by using the following methods:

public void AddMethod(string methodName, MulticastDelegate body);
public void AddMethod(string methodName, CustomDelegate body, 
    Type returnType,   params Type[] parameters);

The first overload allows you to add a method using an existing delegate type (such as a delegate type that exists at compile time), while the second overload allows you to define a method with a particular signature and method body. I'll go over each example below:

Using an Existing Delegate Type

public delegate int MathOperation(int a, int b);

public class Program
{
   public static void Main()
   {
       // Define the method body
       MathOperation body = delegate(int a, int b) { return a + b; };

       // Create an empty DynamicObject
       // that wraps around a System.Object
       DynamicObject dynamic = new DynamicObject();
       dynamic.AddMethod("Add", body);

       // This will return '2'
       int result = (int)dynamic.Methods["Add"](1, 1);
   }
}

In this example, I took an anonymous delegate of the type MathOperation and gave it a method body. Using that body, I then created a method named Add on DynamicObject. Once the method was added, the only thing left to do was invoke it, giving 2 as the result. Using an existing delegate type can be handy if you already have an instance of a particular delegate in memory (such as an anonymous delegate) and you want to make it a part of DynamicObject. The only problem with this approach is that it only allows you to add methods based on delegate types that exist at compile time. This can prevent a developer from generating methods at runtime. That is where the second overload can be used:

Using the CustomDelegate Type

// Note: The differences are highlighted in bold
public class Program
{
   public static void Main()
   {
       // Notice there isn't a preexisting
         // MathOperation delegate in this example
       CustomDelegate body = delegate(object[] args)
       {
          int a = (int)args[0];
          int b = (int)args[1];
          return a + b;
       };

       // Create an empty DynamicObject
       // that wraps around a System.Object
       DynamicObject dynamic = new DynamicObject();
       Type[] parameters = new Type[] {typeof (int), typeof (int)};
       Type returnType = typeof (int);

       dynamic.AddMethod("Add", body, returnType, parameters);

       // This will return '2'
       int result = (int)dynamic.Methods["Add"](1, 1);
   }
}

Using the CustomDelegate type allows me to define the body of the method without tying the method signature to any specific parameter types. In this example, I extracted the first two arguments from the args array defined in the body of the CustomDelegate instance. Just as I did in the previous example, I then simply added the two numbers together and returned the result. The difference in this example was that I actually had to specify both the method return type and the method parameter types when I used the CustomDelegate type as the method body.

The Choice is Yours

Other than those differences that I just mentioned, the two overloads work the same way. It's really up to you to decide whether you prefer the flexibility of CustomDelegate or to use the signature of your own predefined delegate. In my opinion, the second choice would be the better option, since it is more dynamic and thus easier to maintain.

Duck Typing

Another one of LinFu.DynamicObject's strengths is that it can make itself look and act like nearly any interface or non-sealed class. For example, suppose that I have the following interface defined:

public interface IMath
{
   int Add(int a, int b);
}

Walking Like a Duck

Drawing on the examples from the previous section, I can take the same DynamicObject and make it look and act like IMath:

IMath math = null;

// Make sure that the dynamic object
// can indeed act like an IMath object
int result = 0;
if(dynamic.LooksLike<IMath>())
{
   math = dynamic.CreateDuck<IMath>();
   result = math.Add(1, 1);
}

Every time the CreateDuck() method is called, DynamicObject generates a proxy that implements the given class or interface (e.g. IMath). That proxy, in turn, forwards all of its calls back to DynamicObject to handle the actual method implementation. So, every time the IMath.Add() method is invoked by the client, the proxy is calling DynamicObject directly using the following method call:

// This is equivalent to IMath.Add(1, 1)
return dynamic.Methods["Add"](1,1);

A Duck or Not a Duck?

In addition to acting like an IMath instance, DynamicObject can also determine whether or not it has a sufficient number of compatible methods and properties to implement that interface using the LooksLike() method. This is useful if you want to make sure that DynamicObject adheres to a specific interface type.

Mixins

LinFu.DynamicObject also supports "mixing" (or combining) itself with other object instances to make it appear as if all of the mixed items form a cohesive class at runtime. In general, it supports three types of mixins:

Standard Object References

A DynamicObject instance can mix itself with another object reference (that is, a plain old System.Object-derived class), take all of the methods and properties of that class and make it a part of that DynamicObject instance itself. For example, suppose that I wanted to implement an interface named IPerson:

public interface IPerson
{
   string Name { get; }
   int Age { get; }
}

...and suppose that I wanted to implement that interface using instances of two hypothetical mixins named Nameable and HasAge:

public class Nameable
{
   private string _name;

   public Nameable(string name)
   {
     _name = name;
   }
   public string Name
   {
     get { return _name;  }
   }
}

public class HasAge
{
   private int _age;
   public HasAge(int age)
   {
     _age = age;
   }
   public int Age
   {
      get { return _age; }
   }
}

As you can see, there's really nothing special about these two mixins. Creating an implementation of the IPerson interface is as simple as:

DynamicObject dynamic = new DynamicObject();

IPerson person = null;

// This will return false
bool isPerson = dynamic.LooksLike<IPerson>();

// Implement IPerson
dynamic.MixWith(new HasAge(18));
dynamic.MixWith(new Nameable("Me"));

// Now that it's implemented, this
// will be true
isPerson = dynamic.LooksLike<IPerson>();

if (isPerson)
   person = dynamic.CreateDuck<IPerson>();

// This will return "Me"
string name = person.Name;

// This will return '18'
int age = person.Age;

The above example would work for simple cases where the mixins are simply property holders, but what if a mixin had more complicated operations that needed to have access to DynamicObject?

Mixin-Aware Instances

For more advanced scenarios, a mixin itself might need to interact with the other mixins that are a part of the current DynamicObject instance. If you need to have your mixins interact with DynamicObject itself, all you need is to implement the following interface with your mixin:

public interface IMixinAware
{
    DynamicObject Self { get; set; }
}

public class MyMixin : IMixinAware
{
  private DynamicObject _self;
  public void DoSomething()
  {
    DynamicObject dynamic = Self;
    // Perform some operations on the DynamicObject
    IMath math = dynamic.CreateDuck<IMath>();
    math.Add(1, 1);
  }
  public DynamicObject Self
  {
    get { return _self; }
    set { _self = value;}
  }
}

public class MathMixin : IMath
{
   public int Add(int a, int b)
   {
      return a + b;
   }
}

The next thing that you have to do is call the MixWith() method using your new mixin class:

// ...somewhere in your program...
DynamicObject dynamic = new DynamicObject();
dynamic.MixWith(new MathMixin());

// Note: Once the MixWith() method is called,
// the DynamicObject will actually call
// IMixinAware.Self = this; on the new mixin
dynamic.MixWith(new MyMixin());

Any mixin class that implements the IMixinAware interface has the ability to access (and modify) the attached DynamicObject at will and at any given time. In my opinion, the power that this feature gives is mind-boggling. For now, I think I'll leave it in the capable hands of the readers to figure out what to do with this. Anyway, let's move on to the next part: mixing DynamicObjects with other DynamicObjects.

Combining DynamicObjects

Combining two DynamicObjects is as easy as:

public class GreeterMixin
{
    public void Greet()
    {
       Console.WriteLine("Hello, World!");
    }
}
public class OtherMixin
{
    public void DoSomething()
    {
       Console.WriteLine("DoSomething() called");
    }
}

class Program
{
    static void Main(string[] args)
    {
       DynamicObject first = new DynamicObject(new GreeterMixin());
       DynamicObject second = new DynamicObject(new OtherMixin());

       // The two DynamicObjects can combine into a single object
       DynamicObject combined = first + second;
       combined.Methods["Greet"]();
       combined.Methods["DoSomething"]();
    }
}

When two DynamicObjects are combined, it results in a new DynamicObject that has all of the methods and mixins of both previous instances. This can cause a problem, however, if two dynamic objects have two of the same type of mixins. In the event of a mixin conflict, which mixin gets used?

Resolving Conflicts with Duplicate Mixins

There's no easy way to resolve this issue. In fact, this is probably one of the reasons why Microsoft left mixins and multiple inheritance out of the CLR in the first place. So, in the absence of a perfect solution, I decided to take the simplest one and apply it to LinFu.DynamicObject: The first mixin in DynamicObject is the first to execute. Here's an example:

public class GreeterMixin
{
    private string _message;
    public GreeterMixin(string message)
    {
      _message = message;
    }
    public void Greet()
    {
       Console.WriteLine(_message);
    }
}

public static class Program
{
   public static void Main()
   {
      GreeterMixin first = new GreeterMixin("Hello, World!");
      GreeterMixin second = new GreeterMixin("Hello, CodeProject!");

      DynamicObject dynamic = new DynamicObject();
      dynamic.MixWith(first);
      dynamic.MixWith(second);

      // This will display "Hello, World!"
      dynamic.Methods["Greet"]();
   }
}

In this scenario, the first GreeterMixin will always be the first and only mixin to execute. This is because DynamicObject will search all of its mixins and methods for the first method that has the same method name and a compatible method signature and it will execute just that one method. Every other method after the first method found will simply be ignored.

Points of Interest

Bypassing Sealed Methods and Types: "Duck Taping"

One problem that has dogged many developers when using various class libraries is when the given library author seals a type and prevents its methods from being overridden. In normal circumstances (that is, in statically typed languages), this is akin to hitting a brick wall. Fortunately, this isn't the case with LinFu.DynamicObject. Generally, you can bypass a sealed type using the following steps:

Create (or Find) an Interface That Looks Like Your Target Type

The first thing that you need to do is figure out which methods or properties you will override in the target class. Then create an interface that is similar to the list of methods that you will be overriding. In this example, suppose that I wanted to override System.Data.SqlConnection so that I got a notification every time I called IDbConnection.Open(). Since all connections inherit from System.Data.IDbConnection (and, more importantly, they all look like IDbConnection), I can use that interface as the type I will be using for duck typing.

Wrap the Sealed Class Instance and Create the Duck

DynamicObject dynamic = new DynamicObject(new SqlConnection());

// Note that this action doesn't actually cast it to an IDbConnection;
// it just makes it *act* like one
IDbConnection duck = dynamic.CreateDuck<IDbConnection>();

Override the Duck

Next, we need to create an interceptor that wraps around the intended target method or methods. In this case, we want to wrap IDbConnection.Open():

public class CustomInterceptor : IInvokeWrapper
{
   private object _target;
   public class CustomInterceptor(object target)
   {
      _target = target;
   }
   public void BeforeInvoke(InvocationInfo info)
   {
      if (info.TargetMethod.Name != "Open")
          return;

      // Add the additional behavior here
      Console.WriteLine("Open Method Called!");
   }

    public object DoInvoke(InvocationInfo info)
    {
        // Call the original implementation
        object result = info.TargetMethod.Invoke(_target, info.Arguments);

        return result;
    }

    public void AfterInvoke(InvocationInfo info, object returnValue)
    {
        // Do nothing
    }
}

Once the interceptor has been defined, the next thing to do is create a proxy for IDbConnection. Then tie the duck and the CustomInterceptor together into one instance:

ProxyFactory factory = new ProxyFactory();
IDbConnection unsealed = 
    factory.CreateProxy<IDbConnection>(new CustomInterceptor(duck));

// Once the Open() method is called, it will now print "Open Method Called!"
unsealed.Open();

...and that's how you unseal a sealed type.

Interceptor by Design

For those of you who use LinFu's DynamicProxy, one thing that you might find interesting is that DynamicObjects are interceptors themselves. This means that any DynamicObject can act as an interceptor for any proxy created by LinFu.DynamicProxy's ProxyFactory class. For example, suppose that I already have an existing proxy in memory and I want to create a dynamic interceptor so that I can attach it to the proxy:

ProxyFactory factory = new ProxyFactory();
IMath math = factory.CreateProxy<IMath>();

// Create the dynamic interceptor
DynamicObject interceptor = new DynamicObject();

// Attach it to the proxy
IProxy proxy = math as IProxy;
proxy.Interceptor = interceptor;

// Now the proxy is pointing back to the DynamicObject;
// This is where you would start dynamically adding methods
// to that DynamicObject
...

This approach makes it easy to incrementally build an interceptor implementation as the application is running. The possibilities are endless.

Using DynamicObject with C# 3.0

Another realm of possibilities opens up once you combine the abilities of LinFu.DynamicObject with the upcoming language features of C# 3.0. Some of these possibilities are:

Mixins Using Anonymous Types

Now that C# 3.0 has support for anonymous types, dynamically creating throwaway interface implementations is easy. Using an anonymous type, I can easily provide an implementation for IPerson by using the following code:

DynamicObject dynamic = new DynamicObject();
dynamic.MixWith(new {Name="Me", Age=18});
IPerson person = dynamic.CreateDuck<IPerson>();

// Print the name and age
Console.WriteLine("Name = {0}", person.Name);
Console.WriteLine("Age = {0}", person.Age);

Shorter Anonymous Delegates with Lambda Expressions

While in some ways, being able to write shorter anonymous delegates using lambda expressions is no more than syntactic sugar, using lambda expressions with LinFu.DynamicObject (in my opinion) makes the code a bit easier to read. For example, using the previous example with the MathOperation type, I can rewrite it using lambda expressions so that it looks something like:

public delegate int MathOperation(int a, int b);

public class Program
{
   public static void Main()
   {
       // Define the method body
       MathOperation body = (a, b)=> { return a + b; };

       // Create an empty DynamicObject
       // that wraps around a System.Object
       DynamicObject dynamic = new DynamicObject();
       dynamic.AddMethod("Add", body);

       // This will return '2'
       int result = (int)dynamic.Methods["Add"](1, 1);
   }
}

Saving Space

At first, saving yourself from typing a few characters doesn't seem to be a marginal gain in efficiency, but what if the IMath class were defined as:

public interface IMath
{
   int Add(int a, int b);
   int Subtract(int a, int b);
   int Multiply(int a, int b);
   int Divide(int a, int b);
}

What was once one small declaration has turned itself into four and you have effectively increased the amount of items you have to type by 400%. What if you had to type even more than that? Suffice it to say, once you've gotten past ten repetitive anonymous delegate declarations, you'll be well past the point of tedium. For the sake of simplicity, however, let's stick to the four operations mentioned above.

The next thing that I have to do is redefine all of the operations performed by the IMath class:

MathOperation add = (a, b) => { return a + b; }
MathOperation subtract = (a, b) => { return a – b; };
MathOperation multiply = (a, b)=> { return a * b; };
// Divide by zero check omitted for brevity
MathOperation divide = (a, b)=> { return a / b; }; 

Once again, in DynamicObject, I have to add the new methods and implement the interface:

DynamicObject dynamic = new DynamicObject();
dynamic.AddMethod("Add", add);
dynamic.AddMethod("Subtract", subtract);
dynamic.AddMethod("Multiply", multiply);
dynamic.AddMethod("Divide", divide);

IMath math = dynamic.CreateDuck<IMath>();

// Perform all of the operations
Console.WriteLine(math.Add(1, 1));
Console.WriteLine(math.Subtract(1, 1));
Console.WriteLine(math.Multiply(1, 1));
Console.WriteLine(math.Divide(1, 1));

As you can see here, being able to use lambda expressions in place of full-length anonymous delegate declarations can save quite a lot of time and typing. As for the rest of the code above, well, if you've gone this far into the article, then everything I mentioned above is practically boilerplate. It's almost as if dynamically adding methods were nothing new, but I digress.

Parsing LINQ Expression Trees

With LINQ coming up on the horizon, LinFu.DynamicObject naturally makes for a near-perfect expression tree visitor. Here's a simple (and hypothetical) example of a visitor using DynamicObject:

public class ExampleVisitor
{
   ...
   public void DoVisit(Expression expression)
   {
      // Let the DynamicObject decide which method to call
      DynamicObject dynamic = new DynamicObject(this);
      dynamic.Methods["Visit"](expression);
   }
   public virtual void Visit(MethodCallExpression expression)
   {
      // Do something useful here
   }
   public virtual void Visit(BinaryExpression expression)
   {
      // Do something useful here
   }
   ...
}

In this example, I wrapped ExampleVisitor using the DynamicObject class and I let DynamicObject determine which overload of the Visit() method will handle the current LINQ Expression. Once the client code calls DoVisit(), DynamicObject will walk the tree, leaving you to do something useful with the expression metadata.

LinFu.Reflection Features Still on the Drawing Board

The Wish List

Now that you have LinFu in your hands, here is a list of things that I would love to implement, but frankly, I don't have enough time or expertise to effectively come up with a solution to. If you have any personal projects that are similar to what I'm doing here, it would certainly be a pleasure for me to compare notes with you on these subjects. Anyway, here's the list:

Anonymous Interfaces

One of the problems in using LinFu.DynamicObject is that, aside from using the LooksLike() method, there is currently no way to tell which methods and properties DynamicObject implements. It would be nice to be able to have DynamicObject dynamically generate some type information that would allow it to describe its current internal state. This is where the concept of anonymous interfaces comes in. I can have each DynamicObject generate a nameless type that describes its current interface. I can use these dynamically generated anonymous interfaces and compare them with each other and test them for equality. Until I can figure out how to do this, it is nearly impossible to compare two dynamic objects and determine if they share the same methods and properties.

Differential Interfaces

Have you ever wanted to take two classes, put them side by side, and see which properties and methods they have in common? For example, let's say that I wanted to compare an ASP.NET button control with a standard Windows Forms button control. I want to be able to immediately determine which members these two classes have in common so that I can dynamically generate an interface to use for duck typing, employing those common members as part of the newly generated interface. The difference here is that I want this information dynamically generated at runtime and in memory, instead of having something that's scrawled on scraps of paper or a whiteboard that I cannot use.

Type Coercion Using TypeConverters

The general idea here is to modify DynamicObject to be smart enough to figure out whether or not a given runtime argument is compatible with a method signature by checking to see if that argument has a value that is convertible to the target argument type. In other words, I want to make DynamicObject smart enough to convert strings to enums and vice versa, as well as be able to convert integers to their numeric values and vice versa.

Invoking Late-bound Generic Methods with Open Type Parameters

LinFu.DynamicObject works well with generic methods with closed (read: assigned) type parameters, but right now there's no way to invoke a method if the class has something like the following signature:

public class SomeClass
{
   public void DoSomething<T>()
   {
   }
}

In its current state of development, LinFu.DynamicObject cannot invoke the DoSomething<T>() method because there is currently no way to tell DynamicObject that I am looking for a method with an open generic type parameter and that I want to instantiate it using the type parameter that I'm assigning to it. This process is rather complicated and it will take me quite a while to figure out how to implement.

Coming Up in the Next Article

In Part III of this series, I'll show you how you can use LinFu.Delegates to define closures with lambda arguments. I'll also show you how to use the EventBinder class to handle any event, regardless of the delegate signature used by any particular event. Here's an example:

MathOperation add = delegate(int a, int b) { return a + b; };

// Note: this closure will only be evaluated
// once the Invoke() method is called
Closure doMath = new Closure(add, new Closure
    (add, new Closure(add, 12, 9), 11), Args.Lambda);

// What's the answer?
// It's 42, of course! :)
int result = (int) doMath.Invoke(20);

...and suppose I wanted to use EventBinder to bind to a Windows Forms Button.Click event:

Button button = new Button();
CustomDelegate handler = delegate { Console.WriteLine("Button Clicked!"); };
EventBinder.BindToEvent("Click", button, handler);

...and if I really wanted to get fancy, I could even use Closures with LinFu.DynamicObject:

// Derive a new closure from the Add() method that takes a single argument
// and adds '1' to it
Closure lazyMathOperation = new Closure
    (dynamic.Methods["Add"], 1, Args.Lambda);

// This will return '6'
int result = (int) lazyMathOperation.Invoke(5);

One Last Word Before the Next Article

Now that I'm effectively done discussing LinFu's dynamic language extensions, my challenge to you as a fellow developer is to take this language's features and make something useful out of it. While you find something to do with it, I'll be finishing the rest of Part III. Stay tuned!

History

  • 26 October, 2007 -- Original version posted
  • 5 November, 2007 -- Article and downloads updated
  • 12 November, 2007 -- Source and binary downloads updated

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here