如何在c#中使用泛型转换器的映射
本文关键字:泛型 转换器 映射 | 更新日期: 2023-09-27 18:13:06
我正在构建一个辅助函数来将对象的属性映射到对象,该对象知道该属性绑定到哪个UI组件以及如何来回转换它们的值。
现在我需要映射本身,将属性名称(String类型)映射到它们的ModelFieldItems,其中包含一个通用转换器(例如StringToBoolConverter)。但是由于缺少<?>
运算符,我习惯使用java,所以我不能简单地编写private Dictionary<String, ModelFieldItem> items = new Dictionary<String, ModelFieldItem<?,?>>();
其中ModelFieldItem包含类型Information和转换器本身。
因此,如果我想向映射添加一个ModelFieldItem,我需要添加特定的Types,但是它们的类型不同。我已经尝试使用dynamic
或object
作为类型参数,但迟早我会达到一个点,它不起作用。
使用c#中的技巧-在一个列表中包含多个泛型类型,我让编译器很高兴,但我需要一个类型转换来访问我的转换器逻辑,这导致了以下条件
if (item is ModelFieldItem<dynamic, dynamic>)
{
ModelFieldItem<dynamic, dynamic> dynamicItem = (ModelFieldItem<dynamic, dynamic>)item;
总是解析为false
。
编辑
开始转换的方法不知道要转换哪种类型,以及需要转换哪种类型。我们使用循环遍历属性并启动相应的转换器。因此,在转换时不能对不同的类型进行强制转换,因为我们无法知道它们。
我们的转换器非常简单,继承了下面的abstract class
public abstract class TypedConverter<A, B>
{
public abstract B convertFrom(A value);
public abstract A convertTo(B value);
}
如前所述,我有java背景,所以我想在java
中实现的内容大致如下所示private Map<String, ModelFieldItem<?,?>> converters = new HashMap<>();
和
差不多converters.put("key", new Converter<String, Boolean>());
如何在c#中实现这一点?
通常当您遇到这种类型的问题时,您必须使用不需要泛型类型的类的抽象。
如果你的ModelFieldItem
实现了一个没有泛型参数的接口,你可以使用它。
var _dict = new Dictionary<String, IModelFieldItem>()
{
new Converter<string, bool>() // If it's implement IModelFieldItem
};
(YouWillHaveToCastUnlessYouUseDynamicType)_dict[key].Convert("true");
否则,另一种方法是将Dictionary<String, ModelFieldItem>
中的ModelFieldItem
替换为object
或dynamic
,然后您可以在访问字典中的值时强制转换它。
var _dict = new Dictionary<String, object>()
{
new Converter<string, bool>()
};
// You can avoid the cast too by using dynamic
// But it will cost you some perf
((Converter<string, bool>)_dict[key]).Convert("true");
如果你知道你想要的类型。
你可以这样做:
var _dict = new Dictionary<String, object>()
{
new Converter<string, bool>()
};
public void Convert<TToConvert, TConverted>(string key, TToConvert valueToConvert, out TConverted valueConverted)
{
valueConverted = (T)_dict[key].Convert(valueToConvert);
}
bool value;
Convert("Key", "true", out value);
下面是你可以做的另一个例子:
public static void Convert<TToConvert, TConverted>(TToConvert valueToConvert, out TConverted valueConverted)
{
// You should put the dictionary outside of the method
// To avoid to instance it, each time you call this method
var dict = new Dictionary<Type, Func<object, object>>()
{
{ typeof(Tuple<string, int>), x => int.Parse((string)x) },
{ typeof(Tuple<string, bool>), x => bool.Parse((string)x) }
};
valueConverted = (TConverted)dict[typeof(Tuple<TToConvert, TConverted>)](valueToConvert);
}
static void Main(string[] args)
{
bool boolTest;
Convert("false", out boolTest);
Console.WriteLine(boolTest);
int intTest;
Convert("42", out intTest);
Console.WriteLine(intTest);
Console.ReadKey();
}
显然,您应该先尝试是否可以转换类型,以及转换是否成功。最后,让Convert
返回一个布尔值来知道它是否成功。
但至少如您所见,进行转换不需要更多的string key
,您可能会感兴趣。在将变量传递给方法时,您还必须确保变量具有正确的类型,否则您将搜索到错误的键。
反射解决方案:
使用上面的方法,你可以这样做:
static void Main(string[] args)
{
object[] parameters = new object[] { "false", true };
typeof(Program).GetMethod("Convert")
// Be sure that the types will create a valid key
.MakeGenericMethod(new Type[] { parameters[0].GetType(), parameters[1].GetType() })
// Change null to your instance
// if you are not calling a static method
.Invoke(null, parameters);
// parameters[1] is an out parameter
// then you can get its value like that
Console.WriteLine(parameters[1]);
Console.ReadKey();
}
带属性,应该是这样的:
object[] parameters = new object[]
{
propertyToRead.GetValue(objectToRead),
propertyToSet.GetValue(objectToSet)
};
typeof(MapperObject).GetMethod("Convert")
.MakeGenericMethod(new Type[]
{
propertyToRead.PropertyType,
propertyToSet.PropertyType
})
.Invoke(mapperInstance, parameters);
propertyToSet.SetValue(objectToSet, parameters[1]);
您可能需要稍微调整一下,因为我没有尝试编译它
我可以给出另一个解决方案,但我既不知道你的继承架构是什么,也不知道你的Converter
是如何工作的