Search code examples
c#genericsfactory-pattern

Is it possible to create a generic factory with constructor with parameters using Activator.CreateInstance()?


Is there any way to achieve a generic factory when the class it returns requires a parameter in the constructor? All my factories look like this:

public static class UserServiceFactory
{
    public static UserService GetService()
    {
        UserRepository userRepository = new UserRepository();
        return new UserService(userRepository);
    }

}

I tried something like this:

 public static TServiceClass GetService<TServiceClass>()
        where TServiceClass : class, new()
    {
        TServiceClass serviceClass = null;

        string repositoryName = typeof (TServiceClass).ToString().Replace("Service", "Repository");
        Type type = Type.GetType(repositoryName);

        if (type != null)
        {
            object repository = Activator.CreateInstance(type);
            serviceClass =  (TServiceClass)Activator.CreateInstance(typeof (TServiceClass), new[]{repository});
        }

        return serviceClass;
    }

However, this of course won't work because I can't use a class that has no parameterless constructor as a generic parameter, but I feel like I am close. I thought about passing the Service class like GetService(Type serviceClassType) but then I can't declare the method's return type and would have to cast it when I call it, which I want to avoid.

Is there another approach? Is this even possible?


Solution

  • You could do something like this, and take both types as type parameters:

    public static TService GetService<TRepository, TService>() where TRepository:new()
    {
        var repository = new TRepository();
        return (TService)Activator.CreateInstance(typeof(TService), repository);
    }
    

    Or, if you want to rely on convention that the service and repository are the same name (just for illustration):

    public static TService GetService<TService>()
    {
        var repositoryName = String.Concat(typeof(TService).Namespace, Type.Delimiter, typeof(TService).Name.Replace("Service", "Repository"));
        object repository = Activator.CreateInstance(Type.GetType(repositoryName));
        return (TService)Activator.CreateInstance(typeof(TService), repository);
    }
    

    This feels not obvious and unintuitive, and perhaps there are a better, more robust solutions available.

    Perhaps using an inversion of control container would suit as a better approach.

    Using a container, I would just resolve UserService and let the container inject the appropiate repository. If UserService is a dependency from something else, let the DI container resolve that, and a modern container will resolve dependencies of dependencies.