1

I need to instantiate a type that MUST have a IConfiguration parameter, but MAY have other parameters.

Currently I use the following code:

Activator.CreateInstance(myType, BindingFlags.CreateInstance, null, new object[] { configuration }, CultureInfo.InvariantCulture);

myType is the type to instantiate. Written like this, it requires a constructor that has exactly one parameter of type IConfiguration. As you can see, I pass the configuration object via object array to satisfy this requirement.

Now there is a new requirement: The constructor of myType can have multiple parameters. One of them has to accept an IConfiguration object. The other parameters can be ignored (or set to default) in this part of the code. How can I achieve this?

Edit:

These are possible types for myType. V1 is the current requirement, V2 is the new requirement. All three variants are valid and need to be instantiated with a configuration object.

public class PluginV1
{
    private readonly IConfiguration configuration;

    public PluginV1(IConfiguration configuration)
    {
        this.configuration = configuration;
    }
}

public class PluginV2_A
{
    private readonly IConfiguration configuration;
    private readonly IExampleService exampleService;

    public PluginV2_A(IConfiguration configuration, IExampleService exampleService)
    {
        this.configuration = configuration;
        this.exampleService = exampleService;
    }
}

public class PluginV2_B
{
    private readonly IConfiguration configuration;
    private readonly IEnvironment environment;

    public PluginV2_B(IConfiguration configuration, IEnvironment environment)
    {
        this.configuration = configuration;
        this.environment = environment;
    }
}
Sandro
  • 2,998
  • 2
  • 25
  • 51
  • I think we're going to need to see more code than this. Show us at least part of the old constructor and new constructor overloads. Is the problem how to accomplish constructor selection? – Robert Harvey Mar 03 '20 at 16:05
  • Previous comments are not correct. You can't ignore the other parameters and must not assume they'll get the default value. Defaults are provided by the compiler, but that can't work when it doesn't know what constructor you invoke. You must add them to the object[] array. – Hans Passant Mar 03 '20 at 16:23
  • I added some possible types for `myType` – Sandro Mar 03 '20 at 16:33
  • Why do you need to use activator to create these objects? Why not just instantiate them normally? – Marie Mar 03 '20 at 17:19
  • @Marie Because the type is not known at compile time. – Sandro Mar 04 '20 at 14:19

1 Answers1

3

Maybe I'm missing something, but you can use simple reflection to get right constructor, in case there are more than one, and explore its input parameters.

    public class Config : IConfiguration{}

    static void Main(string[] args)
    {
        var config = new Config();

        var o1  = Instantiate(typeof(PluginV1), config);
        var o2  = Instantiate(typeof(PluginV2_A), config);
        var o3  = Instantiate(typeof(PluginV2_B), config);
        Console.WriteLine(o1);
        Console.WriteLine(o2);
        Console.WriteLine(o3);
        Console.ReadKey();
    }

    private static object? Instantiate(Type type, IConfiguration config)
    {
        var targetConstructor =  type.GetConstructors().First();
        // in case there are several .ctors we can find suitable
        //        .First(info => info.GetParameters().Any(parameterInfo =>
        //            typeof(IConfiguration).IsAssignableFrom(parameterInfo.ParameterType)));
        var parameters = targetConstructor.GetParameters().Select(info =>
        {
            if (!typeof(IConfiguration).IsAssignableFrom(info.ParameterType))
            {
                return GetDefault(info.ParameterType);
            }

            return config;
        }).ToArray();
        var instance = Activator.CreateInstance(type, parameters);
        return instance;
    }

    // getting default values https://stackoverflow.com/a/353073/517446
    public static object GetDefault(Type type)
    {
        if(type.IsValueType)
        {
            return Activator.CreateInstance(type);
        }
        return null;
    }
Irdis
  • 919
  • 9
  • 16