Is it possible to create a list of generic delegates, like System.Converter<TInput, TOutput>
?
In java that could be List<Converter<?,?>>
, but with Java's type erasure that's not very useful. Seems like in C# it should be possible to query the generic types after retrieval from the list and use the obtained instance to perform the desired conversion.
Elaborating on the question:
I have user supplied data in user provided formats which I know nothing about at compile time. Algorithms in my program know how to work with its own specific data types, of course. The goal is to have an equiavalent of Java's List<Converter<?,?>>
where Converters can be installed (by me, or users) and queried automatically by the program to see if user's data can be converted to the required format for an algorithm.
This is very rudimentary and is only intended to show the basic concept.
It has:
Register
method to use for registering converters. Registering the same pair of types overwrites a previously registered converter. You can easily change it to be a no-op or an exception.Convert
method to call a registered converter. It would be very trivial to create a TryConvert
method if you needed it.public static class ConverterContainer
{
private static readonly Dictionary<(Type, Type), Delegate> _converters = new Dictionary<(Type, Type), Delegate>();
public static void Register<TInput, TOutput>(Func<TInput, TOutput> converter)
{
if (converter is null)
throw new ArgumentNullException(nameof(converter));
_converters[(typeof(TInput), typeof(TOutput))] = converter;
}
public static TOutput Convert<TInput, TOutput>(TInput input)
{
if (_converters.TryGetValue((typeof(TInput), typeof(TOutput)), out var del))
{
Func<TInput, TOutput> converter = (Func<TInput, TOutput>)del;
return converter(input);
}
throw new InvalidOperationException("Converter not registered.");
}
}
What it does not have:
How to use it:
At startup of your application, register converters, like registering services for dependency injection.
ConverterContainer.Register<long, int>(l => (int)l);
// ... etc.
And wherever you want to perform conversion between a registered pair of input/output types:
int x = ConverterContainer.Convert<long, int>(1000L)
Unfortunately, you do have to specify both type arguments here.
Addition from the OP:
To not have to specify the input parameter type (which is kind of the point of having a dynamic list of available conversions) use the following additional method in the sample ConverterContainer
above (as @madreflection has suggested in the comments himself) :
public static TOutput Convert<TOutput>(object toConvert) {
if (toConvert is null)
throw new ArgumentNullException(nameof(toConvert));
if (Converters.TryGetValue((toConvert.GetType(), typeof(TOutput)), out Delegate conv)) {
object o = conv.DynamicInvoke(toConvert);
return (TOutput) o;
}
throw new InvalidOperationException($"Converter not registered for types: {toConvert.GetType().Name} -> {typeof(TOutput).Name}");
}
You can now throw in any random object
instance and see if the conversion to your desired type is possible.
In C# all the converters in the list will share the same type parameters (TInput
, TOutput
). Unless you have a non generic interface IConverter
, you will not be able to store multiple converter in anything else than a list of object.
You can anyway try to cast the elements into what you need like it is done here (with a dictionary). Hirerate trough an heterogeneous and type-safe dictionary
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With