Dictionary :如何使用T作为Func的generics类型?

我不知道如何清楚地表达它。

我有这个界面:

interface IConverter { Dictionary<Type, Func> ConversionMethods { get; } } 

基本上,它定义了一个契约,说实现它的类应该为它使用的所有自定义类型(无论是枚举还是其他任何东西)提供转换方法。

是否有可能将Func的generics类型中的object替换为相应的字典键的类型(因此不可能有两种不匹配的类型)?

我认为这是不可能的,但替代方案有点烦人(使用dynamicobject ,创建专门的字典……)。


编辑1:虚构的使用例子

 interface IConverter { Dictionary<Type, Func> GetConversionMethods(); } enum A { AA,AB,AC } enum B { BA, BB, BC } class blah : IConverter { public Dictionary<Type, Func> GetConversionMethods() { var d = new Dictionary<Type, Func> { { typeof(A), (s) => { // here, I could return whatever I want because the 'Func' returns 'object' return s == "AA" ? A.AA : s == "AB" ? A.AB : A.AC; } }, { typeof(B), (s) => { // same return s == "BA" ? B.BA : s == "BB" ? B.BB : B.BC; } } }; return d; } void blahah() { // and here, I also get an `object`, where I would like to have a A GetConversionMethods()[typeof(A)]("123"); } } 

这有点复杂,但它确实有效。

首先,您需要在类中封装转换Func ,以便您可以更轻松地处理它们,而不会暴露所有不同类型的参数。 然后,您需要定义接口或基类以隐藏它们会导致问题的各种通用参数,并允许您将不同的转换器放在同一个集合中。 然后,您将需要各种转换器的方法来指示它们使用的类型,而不直接使用这些类型参数。 然后你只需要在一个类中将它全部包装在一个方法中,该方法可以根据需要找到合适的转换器。

我会引导你完成它。

首先,这个基类将是我们处理转换器的方式,而不用担心它的generics类型参数,但仍然知道它使用的类型。

 public abstract class OneWayTypeConverterBase : IConvertFromType, IConvertToType { public abstract Type AcceptsType { get; } public abstract Type ReturnsType { get; } } 

现在我们从该基类inheritance。 这是完成转换实际工作的类; 你可以使用lambda来实例化它,它可以执行你需要的任何转换操作。 请注意,它实现了我们上面定义的属性。

 public class OneWayTypeConverter : OneWayTypeConverterBase { public OneWayTypeConverter(Func conversionMethod) { _conversionMethod = conversionMethod; } public override Type AcceptsType => typeof(TSource); public override Type ReturnsType => typeof(TTarget); private readonly Func _conversionMethod; public TTarget Convert(TSource sourceObject) { return _conversionMethod(sourceObject); } } 

现在我们需要一个地方来容纳所有这些,因此消费代码有一个入口点。 为简单起见,我将它转换为平面转换器集合,然后将它们全部归档到嵌套字典中,以便以后可以进行查找而无需一直调用typeof

 public class TypeConverter { public TypeConverter(IEnumerable converters) { _converters = converters .GroupBy(x => x.AcceptsType) .ToDictionary( kSource => kSource.Key, vSource => vSource .ToDictionary(kTarget => kTarget.ReturnsType, vTarget => vTarget)); } private Dictionary> _converters; public TTarget ConvertType(TSource sourceObject) { Dictionary baseConverters; if (_converters.TryGetValue(sourceObject.GetType(), out baseConverters)) { OneWayTypeConverterBase baseConverter; if (baseConverters.TryGetValue(typeof(TTarget), out baseConverter)) { OneWayTypeConverter converter = baseConverter as OneWayTypeConverter; if (converter != null) { return converter.Convert(sourceObject); } } throw new InvalidOperationException("No converter found for that target type"); } else { throw new InvalidOperationException("No converters found for that source type"); } } } 

所以现在,您可以这样设置:

 var converter = new TypeConverter(new List { new OneWayTypeConverter(x => $"The number was {x}"), new OneWayTypeConverter(x => x != 0), new OneWayTypeConverter(x => $"The bool was {x}") }); 

然后,只要你需要它,你可以像这样使用它:

 var result = converter.ConvertType(4); 

这取决于您可以在多大程度上更改签名,但至少接口可以使用generics类型强制执行。 如何强加类型地添加转换器是实施者的责任,而不是调用者的责任。

实现本身可以使用任何构造来提供转换器。 事件if … else,但是要使用字典下的字典,可以使用Dictionary类型的Dictionary ,其中可以添加强类型转换器。 下面的示例使用帮助程序set函数来确保以预期方式设置字典。

 interface IConverter { Func GetConverter(); //the method returned is always strongly typed, so the caller is never responsible for type checking } enum A{AA,AB,AC} enum B{BA, BB, BC} class blah : IConverter { public Func GetConverter() { if(methods.TryGetValue(typeof(T), out var fn)) //side note, out var fn will not work in older visual studio versions. In that case declare fn before this line return (Func)fn; //the set method ensures that this conversion is safe throw new NotImplementedException(); } public blah() { set(s => s == "AA" ? A.AA : s == "AB" ? A.AB : A.AC); //copied from the example. Enum.Parse could perhaps be used instead set(s => s == "BA" ? B.BA : s == "BB" ? B.BB : B.BC); } Dictionary methods= new Dictionary(); // Delegate can be used as a type to handle all lambda's. It's the implementers responsibility to handle with care. Something like the set helper method is recommended void set(Func fn) //helper method to assign the strongly typed methods to the specific type { methods[typeof(T)] = fn; } } static void blahah() { new blah().GetConverter()("123"); }