List of generic delegates (e.g. System.Converter<TInput, TOutput>)

社会主义新天地 提交于 2021-02-07 14:18:23

问题


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.


回答1:


This is very rudimentary and is only intended to show the basic concept.

It has:

  • A dictionary keyed on a 2-tuple of input and output types to hold converter instances.
  • A 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.
  • A 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:

  • thread safety. This is left as an exercise to a serious implementor.
  • possibly other things I didn't take the time to consider.

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.




回答2:


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




回答3:


No, you can't.

You have to make a Generic Method or Generic class like this:

    void MyMethod<TInput, TOuput>()
    {
        List<Converter<TInput, TOuput>> myList = new List<Converter<TInput, TOuput>>();
    }


来源:https://stackoverflow.com/questions/59369698/list-of-generic-delegates-e-g-system-convertertinput-toutput

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!