The Code:
namespace FSharpIoC
open System
open System.Collections.Generic
open System.Reflection
module FSharpIoCModule =
let rec Resolve requestedType (typeContainer:Dictionary<Type,Type>) =
let newType = typeContainer.[requestedType]
let constructors = newType.GetConstructors()
|> Array.sortBy (fun c -> c.GetParameters().Length)
|> Array.rev
let theConstructor = constructors.[0]
match theConstructor.GetParameters() with
| cstorParams when cstorParams.Length = 0 -> Activator.CreateInstance(newType)
| cstorParams -> cstorParams
|> Seq.map(fun (paramInfo:ParameterInfo) ->
(Resolve paramInfo.ParameterType typeContainer))
|> Seq.toArray
|> theConstructor.Invoke
type Container =
val typeContainer : Dictionary<Type, Type>
new () = {typeContainer = new Dictionary<Type, Type>()}
member x.Register<'a,'b> () =
x.typeContainer.Add(typeof<'a>, typeof<'b>)
member x.Resolve(requestedType) =
FSharpIoCModule.Resolve requestedType x.typeContainer
member x.Resolve<'a> () =
FSharpIoCModule.Resolve typeof<'a> x.typeContainer :?> 'a
Using Our IoC Container From F#:The code below is not exactly a "real world" example, but it does show how our IoC container could be used in an F# application:
module FSharpIoCExample
open System
open FSharpIoC
type Person =
{ FirstName : string
LastName : string}
type IPersonFactory =
abstract CreatePerson : unit -> Person
type PersonFactory() =
interface IPersonFactory with
member x.CreatePerson () =
{FirstName = "TestFirst"; LastName = "TestLast"}
type PersonService =
val personFactory : IPersonFactory
new (personFactory) = {personFactory = personFactory}
member x.GetPerson () =
x.personFactory.CreatePerson()
let iocContainer = new Container()
do iocContainer.Register<IPersonFactory, PersonFactory>()
do iocContainer.Register<PersonService, PersonService>()
let personService = iocContainer.Resolve<PersonService>()
let person = personService.GetPerson()
do Console.WriteLine("The person's name is {0} {1}", person.FirstName, person.LastName)
do Console.ReadLine()
Using Our IoC Container From C#:Here is the same example in C#:
using System;
using FSharpIoC;
namespace CSharpIoCExample
{
class Program
{
static void Main(string[] args)
{
var iocContainer = new Container();
iocContainer.Register<IPersonFactory, PersonFactory>();
iocContainer.Register<PersonService, PersonService>();
var personService = iocContainer.Resolve<PersonService>();
var person = personService.GetPerson();
Console.WriteLine("The person's name is {0} {1}", person.FirstName, person.LastName);
Console.ReadLine();
}
}
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public interface IPersonFactory
{
Person CreatePerson();
}
public class PersonFactory : IPersonFactory
{
public Person CreatePerson()
{
return new Person { FirstName = "TestFirst", LastName = "TestLast" };
}
}
public class PersonService
{
protected readonly IPersonFactory _personFactory;
public PersonService(IPersonFactory personFactory)
{
_personFactory = personFactory;
}
public Person GetPerson()
{
return _personFactory.CreatePerson();
}
}
}
Conclusion
As you can see, it doesn't take much to build a slim featured IoC container in F#. You can find the full source at http://github.com/dmohl/FSharpIoC.
Great post - it's really helpful to have posts connecting terminology and code like this.
ReplyDeleteBTW why not use implicit construction for PersonService?
type PersonService(personFactory: IPersonFactory) =
member x.GetPerson () = personFactory.CreatePerson()
That's a great point! Implicit construction is a more succinct way to accomplish this functionality.
ReplyDeleteThat's a great point! Implicit construction is a more succinct way to accomplish this functionality.
ReplyDelete