如何在c#

时间:2016-10-13 13:40:55

标签: c# generics

我正在构建一个辅助函数来将对象的属性映射到一个对象,该对象知道该属性绑定到哪个UI组件以及如何来回转换它们的值。

现在我需要地图本身,将属性名称(String类型)映射到它们的ModelFieldItems,它们包含一个通用转换器(例如StringToBoolConverter)。但由于缺少<?>运算符,我习惯于从java,我不能简单地写private Dictionary<String, ModelFieldItem> items = new Dictionary<String, ModelFieldItem<?,?>>(); 其中ModelFieldItem包含类型Information和转换器本身。

因此,如果我想将ModelFieldItem添加到地图中,我需要添加特定的类型,但它们的类型不同。 我已经尝试使用dynamicobject作为类型参数,但迟早我会达到一个不起作用的点。

使用来自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#中实现这一目标?

1 个答案:

答案 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> 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

您可能需要稍微调整一下,因为我没有尝试编译它

我可以提供另一种解决方案,但我既不知道你的继承架构是什么,也不知道你的obs.subscribe((e) -> { obs.onNext("BAR"); }) obs.onNext("FOO"); 是如何工作的。