我不知道如何清楚表达。
我有这个界面:
interface IConverter
{
Dictionary<Type, Func<string, object>> ConversionMethods { get; }
}
基本上,它定义了一个契约,说实现它的类应该为它使用的所有自定义类型提供转换方法(无论是枚举还是其他任何东西)。
是否可以将object
的泛型类型中的Func
替换为相应的字典键的类型(因此不可能有两种不匹配的类型)?
我认为这是不可能的,但替代方案有点烦人(使用dynamic
或object
,创建专门的字典......)。
编辑1:虚构的使用例子
interface IConverter
{
Dictionary<Type, Func<string, object>> GetConversionMethods();
}
enum A
{
AA,AB,AC
}
enum B
{
BA, BB, BC
}
class blah : IConverter
{
public Dictionary<Type, Func<string, object>> GetConversionMethods()
{
var d = new Dictionary<Type, Func<string, object>>
{
{
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");
}
}
答案 0 :(得分:2)
这取决于您可以在多大程度上更改签名,但至少接口可以使用泛型类型强制执行。如何强加类型地添加转换器是实施者的责任,而不是调用者的责任。
实现本身可以使用任何构造来提供转换器。事件if ... else,但是为了使用字典下的字典,可以使用类型Dictionary<Type, Delegate>
的字典,其中可以添加强类型的转换器。下面的示例使用帮助器set<T>
函数来确保以预期的方式设置字典。
interface IConverter
{
Func<string,T> GetConverter<T>(); //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<string,T> GetConverter<T>()
{
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<string,T>)fn; //the set<T> method ensures that this conversion is safe
throw new NotImplementedException();
}
public blah()
{
set<A>(s => s == "AA" ? A.AA : s == "AB" ? A.AB : A.AC); //copied from the example. Enum.Parse could perhaps be used instead
set<B>(s => s == "BA" ? B.BA : s == "BB" ? B.BB : B.BC);
}
Dictionary<Type, Delegate> methods= new Dictionary<Type, Delegate>(); // 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<T> helper method is recommended
void set<T>(Func<string,T> fn) //helper method to assign the strongly typed methods to the specific type
{
methods[typeof(T)] = fn;
}
}
static void blahah()
{
new blah().GetConverter<A>()("123");
}
答案 1 :(得分:1)
这有点复杂,但它确实有效。
首先,您需要在类中封装转换Func
,以便您可以更轻松地处理它们,而不会暴露所有不同类型的参数。然后,您需要定义接口或基类以隐藏它们导致问题的各种通用参数,并允许您将不同的转换器放在同一个集合中。然后,您需要各种转换器的方法来指示它们使用的类型,而不直接使用这些类型参数。然后你只需要在一个类中将它全部包装在一个方法中,该方法可以根据需要找到合适的转换器。
我将引导您完成它。
首先,这个基类将是我们处理转换器的方式,而不用担心它的泛型类型参数,但仍然知道它使用的类型。
public abstract class OneWayTypeConverterBase : IConvertFromType, IConvertToType
{
public abstract Type AcceptsType { get; }
public abstract Type ReturnsType { get; }
}
现在我们从该基类继承。这是完成转换实际工作的类;你可以使用lambda来实例化它,它可以执行你需要的任何转换操作。请注意,它实现了我们在上面定义的属性。
public class OneWayTypeConverter<TSource, TTarget> : OneWayTypeConverterBase
{
public OneWayTypeConverter(Func<TSource, TTarget> conversionMethod)
{
_conversionMethod = conversionMethod;
}
public override Type AcceptsType => typeof(TSource);
public override Type ReturnsType => typeof(TTarget);
private readonly Func<TSource, TTarget> _conversionMethod;
public TTarget Convert(TSource sourceObject)
{
return _conversionMethod(sourceObject);
}
}
现在我们需要一个地方来容纳所有这些,因此消费代码有一个入口点。为简单起见,我接受了一个平面转换器集合,然后将它们全部归档到嵌套字典中,以便以后可以进行查找而无需一直调用typeof
。
public class TypeConverter
{
public TypeConverter(IEnumerable<OneWayTypeConverterBase> converters)
{
_converters = converters
.GroupBy(x => x.AcceptsType)
.ToDictionary(
kSource => kSource.Key,
vSource => vSource
.ToDictionary(kTarget => kTarget.ReturnsType, vTarget => vTarget));
}
private Dictionary<Type, Dictionary<Type, OneWayTypeConverterBase>> _converters;
public TTarget ConvertType<TSource, TTarget>(TSource sourceObject)
{
Dictionary<Type, OneWayTypeConverterBase> baseConverters;
if (_converters.TryGetValue(sourceObject.GetType(), out baseConverters))
{
OneWayTypeConverterBase baseConverter;
if (baseConverters.TryGetValue(typeof(TTarget), out baseConverter))
{
OneWayTypeConverter<TSource, TTarget> converter = baseConverter as OneWayTypeConverter<TSource, TTarget>;
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<OneWayTypeConverterBase>
{
new OneWayTypeConverter<int, string>(x => $"The number was {x}"),
new OneWayTypeConverter<int, bool>(x => x != 0),
new OneWayTypeConverter<bool, string>(x => $"The bool was {x}")
});
然后只要你需要它,就可以像这样使用它:
var result = converter.ConvertType<int, string>(4);