如何在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,但是它们的类型不同。我已经尝试使用dynamicobject作为类型参数,但迟早我会达到一个点,它不起作用。

使用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#中实现这一点?

如何在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替换为objectdynamic,然后您可以在访问字典中的值时强制转换它。

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是如何工作的