Home > Back-end >  Choose interface child by some property
Choose interface child by some property

Time:05-11

I have an interface:

public interface IPath 
{
    // Some method 
}

and I have two classes which are inheriting this interface

public class First : IPath { }
public class Second: IPath { }

By the way, in some method I need to choose which class to use, First or Second, it depending on one string property (type), which I get from database. It looks like:

public void SomeMethod(string type)
{
   if (type == "First") { // creating instance of First class }
   else if (type == "Second") { // creating instance of Second class }
   else { ... } 
}

Question is: how can I avoid if/else or switch/case constructions and automaticly create the right instance, depending on the string variable?

CodePudding user response:

You could create a dictionary to map from string to Type and the use that Type and Activator.CreateInstance to create an instance of that type.

Alternatively you could fetch the type using reflection and not need a dictionary at all

private Dictionary<string, Type> _iPathMapping = new Dictionary<string, Type>
{
    { nameof(First), typeof(First) },
    { nameof(Second), typeof(Second) },
};

// ...

public IPath Create(string path)
{
    var type = _iPathMapping[path];
    return (IPath) Activator.CreateInstance(type);
}

(You'd want to extend that code with safety checks, see below)

But this is fundamentally a bad solve. The problem to this is that it's harder to pass parameters to constructors and it's unsafe as well, if any of your implementations don't have a parameterless constructor, this will fail, but not with a compiler error, no it will fail during runtime, i.e once a user (or hopefully testers/ automatic tests) ran into the problem. So a better way would be to store a method that's invoked to construct the type instead of using Activator.CreateInstance, something like

private Dictionary<string, Func<IPath>> _iPathMapping = new Dictionary<string, Func<IPath>>
{
    { nameof(First), () => new First() },
    { nameof(Second), () => new Second() },
};

// ...

public IPath Create(string path)
{
    if (_iPathMapping.TryGetValue(path, out var func))
        return func.Invoke();

    return null;
}

This solves the problem of parameters for the constructor in that it doesn't throw a runtime exception.
But we still haven't solved the problem of actually passing parameters to the constructor, especially when First and Second require different parameters. The only clean* way to I can think of to handle this in a generic and reusable way is using a Dependency Injection framework/ context to actually construct our instances.

But in general, the if/ else if chain or switch statement isn't necessarily a bad thing, you even see it in some places inside .NET


* You could replicate the part of a DI framework that's responsible for resolving dependencies for a constructor, but that's just re-implementing the wheel and might as well save the effort needed and just pull in a dependency like Microsoft.Extensions.DependencyInjection

CodePudding user response:

I have a shorter version as answer, but I saw "MindSwipe" already offered you one:

   Dictionary<string, Type> map = new Dictionary<string, Type>();
   map.Add("First", typeof(First));
   map.Add("Second", typeof(Second));

   var instance = Activator.CreateInstance(map[<your parameter as string>]);
  • Related