The Managed Extensibility Framework (MEF) is a very interesting framework for building extensible applications. It is planned to be used in products such as Visual Studio 2010.
Episode .NET Rocks #398: Glenn Block on MEF is a nice source of information the project.
The MEF Codeplex page has documentation on how to start using it. Also the Addicted to MEF series is a nice source of information on how to start using it.
The example
For this example I want to define a little program F# program that plots math functions. Function definitions will be represented as classes that implement the
IFunction
interface. These definitions will be taken from DLLs stored in a "extensions" directory.Here's the definition of the
IFunction
interface:
type IFunction = interface
abstract Apply : double -> double
end
The following code shows the definition of the Winforms Form that is used to display the function:
open System
open System.Windows.Forms
open System.Drawing
open FunctionDefinitions
open System.ComponentModel.Composition
open System.Collections.Generic
type MyForm() as this = class
inherit System.Windows.Forms.Form()
let mutable functions : IEnumerable<IFunction> = Seq.empty
do this.Size <- new System.Drawing.Size(300,300)
do this.BackColor <- Color.White
do this.Text <- "Functions"
do this.Paint.Add(
fun (pargs:PaintEventArgs) ->
using(new Pen(Brushes.Black))
(fun(p) -> pargs.Graphics.DrawLine(p,
new Point(this.Size.Width/2,0),
new Point(this.Size.Width/2,this.Size.Height))
pargs.Graphics.DrawLine(p,
new Point(0,this.Size.Height/2),
new Point(this.Size.Width,this.Size.Height/2))
Seq.iter (fun func -> pargs.Graphics.DrawLines(p,this.CalcPoints(func))) functions
))
member this.CalcPoints(func:IFunction) : Point array =
{ 0..300} |> Seq.map (fun x -> -1.0/30.0 * double(x) + 5.0) |>
Seq.map (fun x -> x,func.Apply(x)) |>
Seq.map (fun(x,y) -> new Point(int32(x*30.0+150.0),
int32(y*(-30.0)+150.0))) |>
Seq.to_array
[<Import>]
member this.Functions
with get() : IEnumerable<IFunction> = functions
and set (f : IEnumerable<IFunction>) = functions <- f
end
In order to say that we want all available functions(IFunction) identified by MEF we declare the
Functions
property with type IEnumerable<IFunction>
and decorated with the Import
attribute.The following code shows the how we use MEF to compose the application and run the winforms app:
let f = new MyForm()
let catalog = new DirectoryPartCatalog("c:\\temp\\Extensions")
let container = new CompositionContainer(catalog,[||])
container.AddPart(f)
container.Compose()
System.Windows.Forms.Application.Run(f)
Notice that by using
DirectoryPartCatalog
we say that we want to take all extensions(math function definitions) stored in c:\temp\Extensions
. It is important to notice that the program that has the Winforms application is stored in a different assembly from where
IFunction
is defined. The reason is that the extension DLLs need a reference to it.Running this program without extensions shows the following form:
The following code shows an extension written in F#:
#light
namespace MoreFunctions
open FunctionDefinitions
open System.ComponentModel.Composition
[<Export(typeof<IFunction>)>]
type AFunction() = class
interface IFunction with
member this.Apply(x:double) = System.Math.Sin(x)
end
We compile this file and copy the result to the extensions directory.
fsc.exe other.fs -r FunctionDefinitions.dll -r System.ComponentModel.Composition.dll -a
copy other.dll c:\temp\Extensions
By running the program again the following form is shown
Since we're working with common .NET assemblies we can write a function definition using C#, for example:
using FunctionDefinitions;
using System.ComponentModel.Composition;
namespace Test2 {
[Export(typeof(IFunction))]
public class AFunction : IFunction
{
public double Apply(double d)
{
return d*d;
}
}
}
We compile this file and copy it to the extensions directory:
csc /reference:FunctionDefinitions.dll;System.ComponentModel.Composition.dll /t:library AFunc.cs
copy AFunc.dll c:\temp\Extensions
Running this program shows the following form:
Final words
MEF provides a nice/simple way to add extensions to .NET applications. I was really impressed by how simple it is to define Exports and Imports and how little MEF-specific code you have to write in order to make it work.
One thing that will be interesting to see is how this framework is going to interact with things such as the DLR. A nice example of this is Intellipad which, as mentioned in the .NET Rocks interview, is used to allow IronPython extensions.
Code for this post uses MEF Preview 3 release.