通用委托列表(例如 System.Converter<TInput, TOutput>)

Dmi*_*mov 5 c# generics

是否可以创建一个通用委托列表,例如System.Converter<TInput, TOutput>

在 Java 中可能是List<Converter<?,?>>,但是使用 Java 的类型擦除并不是很有用。似乎在 C# 中应该可以在从列表中检索后查询泛型类型并使用获得的实例来执行所需的转换。

详细说明这个问题:
我以用户提供的格式提供了用户提供的数据,而我在编译时对此一无所知。我的程序中的算法当然知道如何使用它自己的特定数据类型。目标是拥有一个等效的 JavaList<Converter<?,?>>转换器,可以在其中安装转换器(由我或用户)并由程序自动查询,以查看用户的数据是否可以转换为算法所需的格式。

mad*_*ion 5

这是非常初级的,仅用于展示基本概念。

它有:

  • 以输入和输出类型的 2 元组为键的字典,用于保存转换器实例。
  • 一种Register用于注册转换器的方法。注册相同的类型对会覆盖之前注册的转换器。您可以轻松地将其更改为无操作或异常。
  • 一种Convert调用注册转换器的方法。TryConvert如果您需要,创建一个方法将是非常简单的。
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.");
    }
}
Run Code Online (Sandbox Code Playgroud)

没有的东西

  • 线程安全。这留给认真的实施者作为练习。
  • 可能还有其他我没有花时间考虑的事情。

如何使用它:

在您的应用程序启动时,注册转换器,例如为依赖注入注册服务。

ConverterContainer.Register<long, int>(l => (int)l);
// ... etc.
Run Code Online (Sandbox Code Playgroud)

以及您想要在已注册的输入/输出类型对之间执行转换的任何地方:

int x = ConverterContainer.Convert<long, int>(1000L)
Run Code Online (Sandbox Code Playgroud)

不幸的是,你必须在这里同时指定类型的参数。


来自 OP 的补充:

为了不必指定输入参数类型(这是拥有可用转换的动态列表的一种方式),请在ConverterContainer上面的示例中使用以下附加方法(正如@madreflection 在自己的评论中所建议的那样):

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}");
}
Run Code Online (Sandbox Code Playgroud)

您现在可以放入任何随机object实例,看看是否可以转换为您想要的类型。