Monday, October 19, 2009

Writing Extension Methods in F# to Use in C#

F#, in my mind, is the language of implementation. It provides a concise syntax when writing code "in the small" that is in many ways superior to C#. This makes F# the ideal language for writing extension methods and other methods that perform operations that are frequently repeated.

Here's a small example on how to write an extension method in F# that takes a generic IEnumerable<T> and outputs each value to the console:

[<System.Runtime.CompilerServices.ExtensionAttribute>]
module public Morton.Extensions.EnumerableExtensions
[<System.Runtime.CompilerServices.ExtensionAttribute>]
let OutputAll (_this:System.Collections.Generic.IEnumerable<'T>) =
for x in _this do System.Console.WriteLine x |> ignore

Note the use of the ExtensionAttribute on both the module declaration and on the function declaration. Both of these are necessary to create the extension method.

In C#, this code could be called just like any other Extension method. First, add the proper using statements to your code, then call the method from any applicable type.

The following code compiles and works properly when referencing an F# assembly containing the above function:

using System.Collections.Generic;
using Morton.Extensions;

namespace ConsoleApplication1
{
static class Program
{
static void Main(string[] args)
{
List<string> list = new List<string>();
list.Add("David");
list.Add("Jennifer");
list.OutputAll();
}
}
}


Now we're cooking. I could see myself using F# for tons of utility methods where writing it in C# would simply be too cumbersome.

UPDATE (10/30/2009)

I received a question today asking how these extensions could also be used from F# as well, with the same syntax. It's not too difficult, but it does require a tiny bit of additional work. The final code would look something like this:

[<System.Runtime.CompilerServices.ExtensionAttribute>]
module public Morton.Extensions.EnumerableExtensions
[<System.Runtime.CompilerServices.ExtensionAttribute>]
let OutputAll (_this:System.Collections.Generic.IEnumerable<'T>) =
for x in _this do System.Console.WriteLine x |> ignore

type System.Collections.Generic.IEnumerable<'T> with
member this.OutputAll() = this |> OutputAll