Search code examples
c#dictionaryfactoryfunc

C# create a new instance with ctor args using a Dictionary and Func


I was wondering whether it was possible to create a new instance of a type given some kind of resolver using a dictionary, where the new type has constructor args. Essentially a factory method.

I have something that works, although I was hoping for a cleaner way to do it. I hit the problem in Java, and thought that was easy in C# - perhaps not!

So it is based around given a dictionary:

Dictionary<Type, Func<ToResolve, Resolved>>

Which has a resolver Func<ToResolve, Resolved> for a given type. I want to map from ToResolve to resolve passing ToResolve fields to ToResolve constructor parameters. ToResolve and Resolve is an abstract class for the scenario. ToResolve in, ToResolve out.

So the working scenario is:

Dictionary<Type, Func<ToResolve, Resolved>> map = new Dictionary<Type, Func<ToResolve, Resolved>>
{
    {
        typeof(ToResolve1), r =>
        {
            var tr = (ToResolve1) r;
            return new Resolved1(tr.x);
        }
    },
    {
        typeof(ToResolve2), r =>
        {
            var tr = (ToResolve2) r;
            return new Resolved2(tr.x);
        }
    }
};

And this can be called as follows:

var toResolve1 = new ToResolve1(100);
var resolved1 = map[toResolve1.GetType()];

var toResolve2 = new ToResolve2("some string");
var resolved2 = map[toResolve2.GetType()];

With the simple class declarations as:

public abstract class Resolved { }

public class Resolved1 : Resolved
{
    public readonly int x;

    public Resolved1(int x) => this.x = x;
}

public class Resolved2 : Resolved
{
    public readonly string x;

    public Resolved2(string x) => this.x = x;
}

public abstract class ToResolve { }

public class ToResolve1 : ToResolve
{
    public readonly int x;

    public ToResolve1(int x) => this.x = x;
}

public class ToResolve2 : ToResolve
{
    public readonly string x;

    public ToResolve2(string x) => this.x = x;
}

Is there more concise way of doing this? Ideally without having to wrap the lambda on a couple of lines and using the explicit casting.

And not using AutoMapper.


Solution

  • I believe you can do what you want by leaving your dictionary as is, but adding separate method to add entries there, like this:

    static class Resolver {
        private static readonly Dictionary<Type, Func<ToResolve, Resolved>> _map = new Dictionary<Type, Func<ToResolve, Resolved>>();
    
        static Resolver() {
            Add((ToResolve1 tr) => new Resolved1(tr.x));
            Add((ToResolve2 tr) => new Resolved2(tr.x));
        }
    
        private static void Add<TToResolve, TResolved>(Func<TToResolve, TResolved> func) where TToResolve : ToResolve where TResolved : Resolved {
            _map[typeof(TToResolve)] = x => func((TToResolve) x);
        }
    
        // the only public interface, non-generic
        public static Resolved Resolve(ToResolve x) {
            return _map[x.GetType()](x);
        }
    }