0

I would like to be able to pass a class name as parameter to a method, and then inside that method create an object of that class with certain parameters.

A concrete (simplified) example:

This is a method to compute an OperationResult

private IOperationResult<Unit> GetFailedOperationResult(IEnumerable<StrictSide> sides, IFailedOperationInfo failedOperationResult)
{
    var exception = failedOperationResult.Exception.HasValue() ? failedOperationResult.Exception.Value() : null;
    if (exception != null)
    {
        return new FailedResult<Unit>(
            new InvalidBundleErrorKeyResolver(new FailedOperationInfo(new OperationInfo(failedOperationResult.OperationName, sides), exception)));
    }
    throw new InvalidOperationException("Failed operation result during bundle consistency check does not contain error or exception.");
}

Depending on the operation that we get the error from, we use different ErrorKeyResolvers. I would like to pass these ErrorKeyResolver as a parameter to the method, so that I don't need to make different GetFailedOperationResult methods for each error type.

Inspired by How to use class name as parameter in C# I tried something like this:

private IOperationResult<Unit> GetFailedOperationResult(IEnumerable<StrictSide> sides,IFailedOperationInfo failedOperationResult, IErrorResourceKeyResolver resourceKeyResolver)
{
    var exception = failedOperationResult.Exception.HasValue() ? failedOperationResult.Exception.Value() : null;
    if (exception != null)
    {
        return new FailedResult<Unit>(Activator.CreateInstance(typeof(resourceKeyResolver),new FailedOperationInfo(new OperationInfo(failedOperationResult.OperationName, sides), exception)));
    }
    throw new InvalidOperationException("Failed operation result during bundle consistency check does not contain error or exception.");
}

But I cannot do typeof(resourceKeyResolver) because I cannot use a variable as a type.

Is there a nice way to do this? Is it even a good thing to do? I also read that dynamics should be avoided so I wonder if saving some code repetition is worth it here.

EDIT: the input parameters should be: private IOperationResult<Unit> GetFailedOperationResult(IEnumerable<StrictSide> sides,IFailedOperationInfo failedOperationResult, string resourceKeyResolver)

And from the class name as string I should be able to find the type.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
Amarela
  • 11
  • 3
  • 1
    `typeof(resourceKeyResolver)` should be `resourceKeyResolver.GetType()`. – MakePeaceGreatAgain Sep 18 '19 at 08:16
  • 1
    You can use [Inversion Of Control](http://joelabrahamsson.com/inversion-of-control-an-introduction-with-examples-in-net/) – Max Sep 18 '19 at 08:21
  • 1
    it doesn't make sens ... why you wana create instance of class represented by `resourceKeyResolver.GetType()` if you have instance of it(`resourceKeyResolver`)? ... this looks like XY problem – Selvin Sep 18 '19 at 08:25
  • @Selvin you are right. Actually I understand the input parameter should just be the class name as a string and not an instance of it. I'll update the question. – Amarela Sep 18 '19 at 08:29
  • @HimBromBeere this indeed fixes the problem of not being able to use a variable as type. Thanks. – Amarela Sep 18 '19 at 08:31
  • 1
    there is overloaded method of `Activator.CreateInstance` which takes 2 strings - assembly and class name ... where is the problem? – Selvin Sep 18 '19 at 08:32
  • Yes it does look like an XY problem – Fabjan Sep 18 '19 at 08:38
  • @Selvin that overload however uses a parameterless constructor: https://learn.microsoft.com/en-us/dotnet/api/system.activator.createinstance?view=netframework-4.8#System_Activator_CreateInstance_System_String_System_String_System_Object___ Whereas in this case I need to specify parameters for the constructor. – Amarela Sep 18 '19 at 08:39
  • But I see I can use reflection to get the Type through the class name as string. So should be possible to make this work. As for this being an XY problem - maybe I went into too many details on the solution I've tried out. Actually I would really like to understand what would be the best solution for going from method with signature 1 to method with signature 2 (as I tried to reflect on the last lines of the question). – Amarela Sep 18 '19 at 08:44

1 Answers1

0

If you pass the class as interface you can use following code to instantiate resolver

var resolver = Activator.CreateInstance(resourceKeyResolver.GetType(),
new FailedOperationInfo(new OperationInfo(failedOperationResult.OperationName, 
sides), exception));

Otherwise, if you use the class name (assembly-qualified-name) you can, for example, convert it to type first and call the above line again

var resolverType = Type.GetType(resourceKeyResolverClassName);
var resolver = Activator.CreateInstance(resolverType ,
new FailedOperationInfo(new OperationInfo(failedOperationResult.OperationName, 
sides), exception));

See here for documentation of GetType() method

Amarela
  • 11
  • 3
tchrikch
  • 2,428
  • 1
  • 23
  • 43
  • 1
    `Type.GetString(resourceKeyResolver);` You mean type `Type.AssemblyQualifiedName`? – MakePeaceGreatAgain Sep 18 '19 at 08:54
  • Yes, updated description and added link to documentation to make it clear – tchrikch Sep 18 '19 at 08:58
  • Ehhhm, now it´s even more confusing. There is no method `GetString` defined, just `GetType`. However you can´t call the latter on an instance of whatever `resourceKeyResolver` actually is. – MakePeaceGreatAgain Sep 18 '19 at 09:08
  • Thank you @tchrikch. As HimBromBeere mentioned, for the second part of the answer I suppose you mean Type.GetType(String) if the class name is given as a string (which is also the link you posted). – Amarela Sep 18 '19 at 09:16