Click here to Skip to main content
15,861,125 members
Articles / Programming Languages / C#
Article

Introducing the LinFu Framework, Part I - LinFu.DynamicProxy: A Lightweight Proxy Generator

Rate me:
Please Sign up or sign in to vote.
4.97/5 (82 votes)
12 Nov 2007LGPL314 min read 373.1K   3.4K   226   70
A fast dynamic proxy library with support for .NET Generics

Introduction

The LinFu Framework is a collection of a few things that I've always wanted to do in .NET, but unfortunately, there weren't any existing implementations that matched all of my needs. So, like any other curious programmer, I set out to write my own library and this is the result. I can ramble on and on about it, but this picture should give you a general idea of what LinFu does and where this series of articles is going:

Screenshot - LinFu_small.png

Note: The word LinFu (Pronunciation: LINN-FOO) is actually an acronym that means "Language INdependent Features Underneath [.NET]." What started as simple tinkering into dynamic proxies and Reflection.Emit four years ago slowly evolved into a personal project in extending existing .NET languages without changing their respective syntaxes. This entire framework is a culmination of those years of my own personal research and, after some deliberation, I've decided to license this whole project under the terms of the LGPL license. This means that everyone will be free to use the library without royalties as long as it adheres to the terms of that license. If you are going to use this in your own commercial projects, then I would absolutely love to get some constructive commercial feedback (and a few professional references wouldn't hurt either).

If there are any features that you think should be added (or any features you want to add yourself), just send me a copy of it so that I can look at it and merge it into the codebase that I currently have with me. Anyway, let's go back to the article…

Language Independent?

LinFu, in its most basic form, is nothing but a managed DLL library that is compiled into IL. Since anything written in IL can be used by any .NET language, all the features of LinFu that I will be showing in the following articles can be consumed by any .NET language. Using this rationale, LinFu extends languages like VB.NET and C# without affecting their respective syntaxes. It supports the following features:

Dynamic Proxies

LinFu has its own proxy generator, which even predates other more prominent implementations such as CastleProject's Dynamic Proxy and Castle DynamicProxy2. This version of LinFu DynamicProxy has supported generics ever since the VS2005 release and, based on my current benchmarks (which will be discussed in this article), it consistently outperforms Castle's implementation by a factor of at least 200%. In short, LinFu's Dynamic Proxy is lean, mean and very, very fast.

Duck Typing and Late Binding

Dynamic languages such as Ruby and Python (and to some extent, VB.NET) have the amazing ability to call methods at runtime without knowing exactly which methods they will be using at compile time. Furthermore, those take that concept even further and actually "infer" which method should be called based on the arguments that are passed to the method at runtime (aka "Duck Typing"). It would be nice if there were a way to do that natively in C# (2.0), but unfortunately, that's not entirely possible... unless you use LinFu, of course! LinFu does some mind-bending tricks with the C# syntax to make it work and, later in the series, I'll show you how it all works behind the scenes.

Ruby-style Mixins

Dynamic languages have the ability to seamlessly and dynamically modify their classes at runtime so that each class can practically add and subtract methods (and even add other classes and interface implementations) to and from each class implementation. Believe it or not, I've found a way to implement the same type of dynamic mixins in both C# and VB.NET and still be able to adhere to the strongly-typed rules of each of both languages! (Well, maybe not VB.NET, since sometimes it can be loosely typed, but I digress). With LinFu, you can incrementally build an object's implementation at runtime just as easily as one can incrementally build an SQL string. Want to dynamically implement an interface at runtime, but don't have a class that implements it at compile time? No problem. LinFu can incrementally stitch methods together to form one cohesive interface implementation, all at runtime.

Delegates with Lambda Arguments (aka, "Currying")

While languages such as C# 3.0 and the upcoming version of VB.NET have support for defining Lambda "Expressions," one feature that I've always wanted to have is the ability to take any delegate and actually hard-bind (or close) one or more of the delegate parameters to a specific arbitrary value. In other words, this feature allows you to "simulate" delegate calls by hard-wiring some (or all) of the arguments passed to a delegate when an event is fired. In other words, LinFu makes it very easy to mock events.

Universal Event Handling

One of the biggest problems in writing an application that uses the MVC pattern (or the MVP pattern) is finding a uniform way to have the controller handle events that are fired from the Presentation/View without knowing "a priori" the signature of those events at compile time. LinFu can actually attach itself to any event fired on any object at runtime, regardless of the delegate signature that event might be using.

A Very, Very Simple IoC Container

Imagine that you had an Inversion of Control (or Dependency Injection) container that didn't rely on an external configuration file (such as an XML file) to assemble itself. Furthermore, suppose that the only "configuration" that you would have to do would involve less than a few lines of code and a simple XCOPY operation in order to get everything up and running. Does it sound too simple? Well, seeing is believing and in Part IV, I'll do my best to make you a believer.

Design by Contract

There have been quite a few attempts at creating a DbC Framework in .NET (and even in Java, with JContract) such as eXtensible C#, Kevin McFarlane's DbC Framework and the like, but few of them have been able to effectively emulate the Eiffel language's DbC mechanism without tying their respective implementations to their particular language (in the case of XC#) or cluttering their domain methods with numerous precondition, postcondition and invariant checks (as is the case with McFarlane's library). LinFu allows you to transparently inject preconditions, postconditions and invariants into your code at runtime, regardless of whether or not you actually have access to the source code of the library you wish to inject. Want to wrap System.Data.IDbConnection in a user-defined contract? No problem, it only takes a single line of code. Want to dynamically generate verifiable contracts at runtime? LinFu can do that, too. When used in combination with the Simple.IoC container, it can even inject contracts into your apps while it's still running, all without recompiling your entire application.

As you can see, LinFu can do quite a few interesting things. In publishing these articles, I hope that I can help a sizeable number of developers out there to write better code through the use of my library. For me, it's always a pleasure to give back to the developer community as a whole and any additional hour that I can spend saving any other developer from having to write more boilerplate code is time well spent. So, without further ado, let's get started!

Background

This article assumes that you're familiar with the concept of dynamic proxies and, for those of you who are interested in figuring out how it all works, I'll do my best to explain it in the next few sections without having to dive into the gory details of Reflection.Emit. If you're a casual developer, however, you can just browse the next few sections to use the code.

DynamicProxy

This article will show you how to use LinFu's DynamicProxy to add "hooks" to your method code so that you can arbitrarily inject new code into your application at runtime. This can be useful if you want to add additional features to your application (such as logging or performance timing), but you don't want to have those additional features to be cluttered across your codebase.

Using the Code

LinFu's DynamicProxy library allows you to intercept any method call made to any virtual method in your object instances and replace those methods with your own implementation at runtime. For example:

C#
public class Greeter
{
    public virtual void Greet()
    {
        Console.WriteLine("Hello, World!");
    }
}

In this example, we're going to wrap the Greet() method and we're also going to replace that greeting with "Hello, CodeProject!"

Choose Your Flavor

With LinFu, there are two styles of interception, as denoted by the following interface definitions:

C#
public interface IInterceptor
{
    object Intercept(InvocationInfo info);
}

public interface IInvokeWrapper
{
    void BeforeInvoke(InvocationInfo info);
    object DoInvoke(InvocationInfo info);
    void AfterInvoke(InvocationInfo info, object returnValue);
}

Wrapping the Greeter Object

Depending on what your needs might be, you'll have to implement one of these two interfaces to create your own interceptor. In this case, we're going to implement IInvokeWrapper so that we can add some additional behavior to the Greeter class before and after its Greet() method is called:

C#
public class GreetInterceptor : IInvokeWrapper
{
    private Greeter _target;
    public GreetInterceptor(Greeter target)
    {
        _target = target;
    }
    public void BeforeInvoke(InvocationInfo info)
    {
        Console.WriteLine("BeforeGreet() called");
    }

    public object DoInvoke(InvocationInfo info)
    {
        // Make our own greeting,
        // and ignore the old one
        Console.WriteLine("Hello, CodeProject!");

        object result = null;

        // Note: If you wanted to call the original
        // implementation, uncomment the following line:
        //result = info.TargetMethod.Invoke(_target, info.Arguments);
        return result;
    }

    public void AfterInvoke(InvocationInfo info, object returnValue)
    {
        Console.WriteLine("AfterGreet() called");
    }
}

The first thing that you might notice here is that the interceptor needs to have a reference to an actual instance of a greeter object. That's because LinFu's dynamic proxies don't actually have a behavior (or an intrinsic state) of their own. Every proxy generated by LinFu dynamically overrides all of its parent's virtual methods. Each one of its respective overridden method implementations delegates each method call to the attached IInterceptor object. In many respects, each proxy is like an empty shell. Without an attached interceptor, a proxy simply throws a NotImplementedException.

A Manually Written Proxy

If I were to write the proxy by hand, it would look something like this:

C#
public class GreeterProxy : Greeter, IProxy
{
    private IInterceptor _interceptor;
    public override void Greet()
    {
        if(_interceptor == null)
        throw new NotImplementedException();

        // The following is pseudocode:
        InvocationInfo info = new InvocationInfo();

        // Note: The actual proxy would fill the info
        // object with the necessary method data
        // Pass the call to the interceptor
        _interceptor.Intercept(info);
    }

    public IInterceptor Interceptor
    {
        get 
        { 
            return _interceptor;  
        }
        set 
        { 
            _interceptor = value; 
        }
    }
}

As you can see here, LinFu's dynamic proxies only redirect calls to an interceptor, despite whatever implementations each proxy might inherit from its original base class. When a proxy is called in place of an actual object, it constructs an InvocationInfo object with all the necessary reflection details. This is so that the interceptor can either call the original implementation of that method or replace it with its own brand new implementation.

InvocationInfo Objects

The InvocationInfo class, in turn, is defined as:

C#
public class InvocationInfo
{
    // … Constructor omitted for brevity
    public object Target
    {
        get 
        { 
            return _proxy; 
        }
    }
    public MethodInfo TargetMethod
    {
        get 
        { 
            return _targetMethod; 
        }
    }
     public StackTrace StackTrace
     {
        get
        {
           return _trace;
        }
     }
     public MethodInfo CallingMethod
     {
        get
        {
           return (MethodInfo) _trace.GetFrame(0).GetMethod();
        }
     }
     public Type[] TypeArguments
     {
        get 
        { 
            return _typeArgs; 
        }
     }
     public object[] Arguments
     {
       get 
        { 
            return _args; 
        }
     }
  //
}

Most of the properties in this class are fairly self-explanatory. The Target property refers to the proxy instance that intercepted the method call, while the StackTrace property refers to the state of the call stack when the proxy was called.

What seems to be missing here, however, is any reference to the actual object that will provide the implementation for this proxy (in this case, a Greeter object). This is done by design. As far as the LinFu library is concerned, a proxy's sole task is to delegate its implementation to an IInterceptor object which may (or may not) have an implementation of the real object as its implementation (i.e. the actual Greeter object). Everything else (from the proxy's standpoint) is irrelevant.

Note: I separated the proxy instance from the proxy implementation (and thus, the real object) to make it easier to manage multiple proxies at once. In theory, a handful of these proxies could be pooled together and reused to save memory, but such a task is way beyond the scope of this article. I'll leave it to the readers to come up with a scheme to make that work.

Putting It All Together

Now that there is a GreetInterceptor class, the only things left to do are to instantiate the proxy and attach it to the greeter:

C#
ProxyFactory factory = new ProxyFactory();
Greeter actualGreeter = new actualGreeter();
GreetInterceptor interceptor = new GreetInterceptor(actualGreeter);
Greeter greeter = factory.CreateProxy<Greeter>(interceptor);

Once the greeter proxy object is instantiated and initialized with the interceptor, the only thing left to do is to run it:

C#
// Since the interceptor is in place, the original "Hello, World!" message
// will be replaced with "Hello, CodeProject!"
greeter.Greet();

After the Greet() method is called, the proxy will redirect the call back to the GreetInterceptor instance, which will provide the replacement implementation for the Greet() method. Aside from the proxy instantiation itself, this process makes intercepting method calls relatively transparent. What if, however, you need to replace the interceptor?

The IProxy Interface

As it turns out, every proxy generated by LinFu implements the IProxy interface. Since the IProxy interface has an Interceptor property, replacing an existing interceptor is as easy as doing the following:

C#
IInterceptor otherInterceptor = new SomeOtherInterceptor();
IProxy proxy = (IProxy)greeter;
proxy.Interceptor = otherInterceptor;

// Call the new implementation
greeter.Greet();

Points of Interest

One thing about LinFu's DynamicProxy that's always fascinated me is its raw speed. On my 1.8GHz Dual-core laptop, it takes only a few milliseconds to generate a single proxy. For the sake of reference, I've done a simple benchmark on LinFu's Dynamic Proxy versus CastleProject's DP2.

Benchmark Setup

This benchmark was run against both LinFu and Castle DP2 libraries with proxy type caching turned off. This was to prevent any of the results from being skewed due to cache hits from either library. For Castle's DynamicProxy2, I had to modify BaseProxyGenerator.cs, line 160, to disable caching:

C#
protected void AddToCache(CacheKey key, Type type)
{
    // NOTE: This has been disabled for benchmarking purposes
    //scope.RegisterInCache(key, type);
}

For LinFu, I disabled the proxy cache from being used by setting its reference to null in Program.cs, line 104, in the benchmarking code:

C#
…
ProxyFactory factory = new ProxyFactory();
// Disable the cache in order to prevent
// the factory from skewing the benchmark results
factory.Cache = null;
…

Benchmark Results

Screenshot - LinFuGraph.png

This test simulates a worst-case scenario where each proxy generator (or factory) effectively has to generate up to one thousand unique proxy types in succession. As the results of this benchmark show, both of these libraries scale quite well when generating up to one hundred unique proxy types. However, Castle's performance significantly slows down once the benchmark passes the one hundred type threshold. In fact, as it approaches the point where it has to generate a thousand unique types, it runs five times slower than LinFu's implementation. Other than that, I think the benchmark speaks for itself.

Since type caching is enabled by default in both libraries, however, multiple calls to generate the same proxy type will be cached and those calls (in practice) will only incur a minimal amount of overhead. Nonetheless, this benchmark shows the speed differences between the two libraries and, as these numbers show, LinFu's DynamicProxy is much faster than its Castle counterpart.

Note: If you should be inclined to perform benchmarks of your own, I've included the benchmarking code as part of the source code, as well as the Excel 2007 spreadsheet that I used to generate that chart for the benchmarks. I've also included a copy of the modified Castle.DynamicProxy2.dll with its caching disabled, just in case someone else on CodeProject would be interested in adding even more benchmarks.

Limitations

There are a few things that LinFu's Dynamic Proxy generator cannot do. These are:

It cannot inherit from sealed types.

By itself, LFDP cannot override a sealed type. However, once I get to discussing LinFu.Reflection, I'll show you how to get around this limitation.

It cannot override non-virtual members.

This one is pretty self-explanatory. Lately, I've been playing around with using the Mono.Cecil library to make non-virtual methods virtual, but so far, I haven't come up with anything robust enough to include in LinFu. I'll update this article once I come up with something reliable.

Special Thanks

Thanks to Jeff Brown and Julien Dagorn, LinFu.DynamicProxy now supports overriding methods with ref and out parameters. Thanks for the input, guys!

License

The entire LinFu Framework is licensed under the terms of the LGPL license, meaning that you are free to use the library DLL in your commercial applications without royalties, but if you make any modifications to its source code, then you must publish the source code so that everyone else can use it.

Coming Up in the Next Article

LinFu's DynamicProxy is only a rudimentary part of what the overall LinFu Framework has to offer. In the next article, I'll show you how you can use the features of C# 3.0 along with LinFu to dynamically generate interface implementations at runtime. I'll give you an example. Suppose that in my domain model, I have the following interface definitions defined:

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

public interface IDogOwner
{
    int NumberOfDogsOwned 
    { 
        get; 
    }
}

Using LinFu's DynamicObject and the new features of C# 3.0 (namely, anonymous types), I can implement these interfaces at runtime using only the following code:

C#
// Notice that this DynamicObject is actually wrapping a *bare* System.Object
// and that there is initially no underlying implementation

DynamicObject dynamicObject = new DynamicObject(new object())

// Implement IPerson
dynamicObject.MixWith(new { Name="Me", Age=18 });

// Implement IDogOwner
dynamicObject.MixWith(new { NumberOfDogsOwned=1 });

// If it looks like a person…
IPerson person = dynamicObject.CreateDuck<IPerson>();

// …then it must be a person!
// This will return "Me"
string name = person.Name;

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

// …or an IDogOwner
IDogOwner dogOwner = dynamicObject.CreateDuck<IDogOwner>();

// This will return '1'
int dogsOwned = dogOwner.NumberOfDogsOwned;

For now, I'll let you digest that last block of code in your mind for awhile. Suffice to say, LinFu allows developers to use dynamic language features from within the comforts of their own statically typed languages. Meanwhile, I'll be off writing the next article. Stay tuned!

History

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

License

This article, along with any associated source code and files, is licensed under The GNU Lesser General Public License (LGPLv3)


Written By
Software Developer (Senior) Readify
Australia Australia
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 5 Pin
CodeBooger11-Dec-12 7:33
CodeBooger11-Dec-12 7:33 
GeneralRe: My vote of 5 Pin
Philip Laureano11-Dec-12 11:14
Philip Laureano11-Dec-12 11:14 
GeneralHow to Create a DynamicProxy from a WSDL Provided to test a WbService and Methods using LinFu Pin
AMDUS27-Apr-11 17:06
AMDUS27-Apr-11 17:06 
GeneralRe: How to Create a DynamicProxy from a WSDL Provided to test a WbService and Methods using LinFu Pin
Philip Laureano27-Apr-11 17:33
Philip Laureano27-Apr-11 17:33 
GeneralRe: How to Create a DynamicProxy from a WSDL Provided to test a WbService and Methods using LinFu Pin
AMDUS2-May-11 12:51
AMDUS2-May-11 12:51 
GeneralRe: How to Create a DynamicProxy from a WSDL Provided to test a WbService and Methods using LinFu Pin
AMDUS2-May-11 13:00
AMDUS2-May-11 13:00 
GeneralRe: How to Create a DynamicProxy from a WSDL Provided to test a WbService and Methods using LinFu Pin
Philip Laureano3-May-11 11:51
Philip Laureano3-May-11 11:51 
GeneralRe: How to Create a DynamicProxy from a WSDL Provided to test a WbService and Methods using LinFu Pin
AMDUS3-May-11 22:37
AMDUS3-May-11 22:37 
GeneralRe: How to Create a DynamicProxy from a WSDL Provided to test a WbService and Methods using LinFu Pin
Philip Laureano3-May-11 22:58
Philip Laureano3-May-11 22:58 
GeneralRe: How to Create a DynamicProxy from a WSDL Provided to test a WbService and Methods using LinFu Pin
AMDUS4-May-11 9:13
AMDUS4-May-11 9:13 
GeneralA Problem About The Attributes of A Property Pin
cocozim28-Feb-11 23:06
cocozim28-Feb-11 23:06 
AnswerRe: A Problem About The Attributes of A Property Pin
Philip Laureano1-Mar-11 0:05
Philip Laureano1-Mar-11 0:05 
GeneralRe: A Problem About The Attributes of A Property Pin
cocozim1-Mar-11 15:15
cocozim1-Mar-11 15:15 
QuestionWhy in debug of this solution i get a different result than the build result. Pin
bhushanvinay28-Jul-10 5:22
bhushanvinay28-Jul-10 5:22 
QuestionDynamic Proxy server or pattern? Pin
proxydev18-Mar-10 12:31
proxydev18-Mar-10 12:31 
AnswerRe: Dynamic Proxy server or pattern? Pin
Philip Laureano18-Mar-10 18:08
Philip Laureano18-Mar-10 18:08 
NewsLinFu.DynamicProxy now on Github! Pin
Philip Laureano16-Mar-10 16:40
Philip Laureano16-Mar-10 16:40 
NewsSilverlight 3.0 Port for LinFu.DynamicProxy Now Available Pin
Philip Laureano16-Mar-10 16:39
Philip Laureano16-Mar-10 16:39 
GeneralGreat!! Pin
Staffan Eketorp27-Jul-09 1:08
Staffan Eketorp27-Jul-09 1:08 
GeneralRe: Great!! Pin
Philip Laureano27-Jul-09 2:35
Philip Laureano27-Jul-09 2:35 
GeneralI don't need StackTrace information (overloads) Pin
Member 29945732-Jun-08 5:58
Member 29945732-Jun-08 5:58 
GeneralRe: I don't need StackTrace information (overloads) Pin
Philip Laureano2-Jun-08 10:54
Philip Laureano2-Jun-08 10:54 
GeneralDo you want to add a DataBinding Part VI [modified] Pin
Member 29945732-Apr-08 0:33
Member 29945732-Apr-08 0:33 
QuestionProblem with Generic Type? Pin
Member 29945731-Apr-08 3:15
Member 29945731-Apr-08 3:15 
AnswerRe: Problem with Generic Type? Pin
Philip Laureano1-Apr-08 11:45
Philip Laureano1-Apr-08 11:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.