I'm trying to write a simple library that can convert between different types. Using .Net Core 3.1.
The purpose of this would be to convert between two classes that inherit from the same base class.
public class SharedBaseClass {}
public class DestinationClass : SharedBaseClass {
public DestinationClass(){}
}
public class InputClass : SharedBaseClass {
public InputClass(){}
}
As such, I have introduced an interface that defines such a converter
public interface IConverter<out TU> where TU : SharedBaseClass
{
public TU Convert(SharedBaseClass input);
}
This interface is then used by the below class to perform the conversions
public class ConverterExecutor
{
private readonly Dictionary<Type, IConverter<SharedBaseClass>> _converters;
public ConverterExecutor(Dictionary<Type, IConverter<SharedBaseClass>> converters)
{
_converters = converters;
}
public IEnumerable<SharedBaseClass> ConvertMultiple(IEnumerable<SharedBaseClass> classesToConvert)
{
var converted = new List<SharedBaseClass>();
foreach(var toConvert in classesToConvert)
{
_converters.TryGetValue(toConvert.GetType(), out var converter);
if (converter != null) {
converted.Add(converter.Convert(toConvert));
continue;
}
converted.Add(toConvert);
}
return converted;
}
}
Clients would then simply create implementations of the IConverter interface to encapsulate conversion logic. An example for a converter from InputClass
to DestinationClass
would is
public class DestinationConverter: IConverter<DestinationClass> {
public DestinationClass Convert(SharedBaseClass input) {
return new DestinationClass();
}
}
To complete the example, I've added a short main method of how these would be setup
public class Program
{
public static void Main()
{
var executor = new ConverterExecutor(new Dictionary<Type, IConverter<SharedBaseClass>>{
// various converters for various types added here
{typeof(InputClass), new DestinationConverter()}
});
var result = executor.ConvertMultiple(new List<SharedBaseClass>{new InputClass()});
Console.WriteLine(result.First());
}
}
This all works, however I am bothered by the fact hat the input parameter of the convert method for an implementation of IConverter is dependent on the base class.
public DestinationClass Convert(SharedBaseClass input)
To fix this, I tried redefining the interface as such:
public interface IConverter<out TU, in T> where TU : SharedBaseClass where T: SharedBaseClass
{
public TU Convert(T input);
}
This refactoring works fine across the involved classes and gives me the correct type within each implementation, however I am getting a compile error within the main method as the signature of the DestinationConverter class is not suitable to be added to the dictionary. I suspect this comes from the fact that the T: SharedBaseClass
parameter has been added as an in
parameter (contravariant) on the IConverter
interface, however leaving it as invariant fails in the same way. I suspect if it would be covariant (not possible with an input param) the compiler would allow this. I'm thinking I got my abstractions wrong somewhere along the way, so what would be a suitable solution in this case?
public class Program
{
public static void Main()
{
var executor = new ConverterExecutor(new Dictionary<Type, IConverter<SharedBaseClass, SharedBaseClass>>{
// various converters for various types to be added here
{typeof(InputClass), new DestinationConverter()}
});
var result = executor.ConvertMultiple(new List<SharedBaseClass>{new InputClass()});
Console.WriteLine(result.First());
}
}
Full refactored example that is failing: https://dotnetfiddle.net/vuDCiH