我正在构建一个辅助函数来将对象的属性映射到一个对象,该对象知道该属性绑定到哪个UI组件以及如何来回转换它们的值。
现在我需要地图本身,将属性名称(String类型)映射到它们的ModelFieldItems,它们包含一个通用转换器(例如StringToBoolConverter)。但由于缺少<?>
运算符,我习惯于从java,我不能简单地写private Dictionary<String, ModelFieldItem> items = new Dictionary<String, ModelFieldItem<?,?>>();
其中ModelFieldItem包含类型Information和转换器本身。
因此,如果我想将ModelFieldItem添加到地图中,我需要添加特定的类型,但它们的类型不同。
我已经尝试使用dynamic
或object
作为类型参数,但迟早我会达到一个不起作用的点。
使用来自C# - Multiple generic types in one list的技巧我让编译器感到高兴,但我需要一个类型转换来访问我的转换器逻辑,这导致我出现以下情况
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#中实现这一目标?
答案 0 :(得分:1)
通常当您遇到此类问题时,您必须使用不需要泛型类型的类的抽象。
如果您的ModelFieldItem
实现了没有通用参数的接口,则可以使用它。
var _dict = new Dictionary<String, IModelFieldItem>()
{
new Converter<string, bool>() // If it's implement IModelFieldItem
};
(YouWillHaveToCastUnlessYouUseDynamicType)_dict[key].Convert("true");
否则,另一种方法是将ModelFieldItem
替换为Dictionary<String, 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
您可能需要稍微调整一下,因为我没有尝试编译它
我可以提供另一种解决方案,但我既不知道你的继承架构是什么,也不知道你的obs.subscribe((e) -> {
obs.onNext("BAR");
})
obs.onNext("FOO");
是如何工作的。