danielwertheim

danielwertheim


notes from a passionate developer

Share


Sections


Tags


Disclaimer

This is a personal blog. The opinions expressed here represent my own and not those of my employer, nor current or previous. All content is published "as is", without warranty of any kind and I don't take any responsibility and can't be liable for any claims, damages or other liabilities that might be caused by the content.

C# - Generic factory with support for private constructors

Today I got involved in a small question on Twitter on how to create a generic factory creating instances of classes having a private constructor. So I put together a small sample that shows two solutions:

The example lets you time the difference between the solutions. And feel free to add the code using IL. Please note! This was something I put together really quick so there's probably some room left for you to tweak.

The source code is also made availible as a Gist here: https://gist.github.com/1529618">https://gist.github.com/1529618

Sample model

public interface IPerson
{
    string Name { get; set; }
}

public class Person : IPerson 
{
    public string Name { get; set; }

    private Person() { }
}

The factory

public static class Factory<T> where T : class 
{
    private static readonly Func<T> FactoryFn;

    static Factory()
    {
        //FactoryFn = CreateUsingActivator();

        FactoryFn = CreateUsingLambdas();
    }

    private static Func<T> CreateUsingActivator()
    {
        var type = typeof(T);

        Func<T> f = () => Activator.CreateInstance(type, true) as T;

        return f;
    }

    private static Func<T> CreateUsingLambdas()
    {
        var type = typeof(T);

        var ctor = type.GetConstructor(
            BindingFlags.Instance | BindingFlags.CreateInstance |
            BindingFlags.NonPublic,
            null, new Type[] { }, null);

        var ctorExpression = Expression.New(ctor);
        return Expression.Lambda<Func<T>>(ctorExpression).Compile();
    }

    public static T Create(Action<T> init)
    {
        var instance = FactoryFn();

        init(instance);

        return instance;
    }
}

The test app

class Program
{
    static void Main(string[] args)
    {
        //TOUCH ONCE BEFORE TIMING
        var touchedPerson = 
            Factory<Person>.Create(p => p.Name = "Daniel");

        for (var a = 0; a < 5; a++)
        {
            var sw = Stopwatch.StartNew();
            for (int c = 0; c < 100000; c++)
            {
                var person =
                    Factory<Person>.Create(p => p.Name = "Daniel");
                var n = person.Name;
            }
            sw.Stop();
            Console.WriteLine(sw.Elapsed.TotalMilliseconds);
        }
        Console.ReadKey();
    }
}

That's it. Enjoy!

//Daniel

View Comments