首页 > 解决方案 > 使用泛型将带有字符串键的字典转换为带有枚举键的字典

问题描述

我有一堆从外部来源读取的字典(以字符串为键)。我想将所有字典转换为具有不同枚举作为键的字典。对于每个枚举,我都有一个将字符串转换为枚举的自定义方法。

与其为每种枚举类型编写一个转换函数,我希望我可以为所有类型编写一个通用方法。

我不太熟悉泛型(或类型检查),但下面的代码是编写函数的尝试。vs-intellisense 不喜欢这种解决方案。有可能做我想做的事吗?如果是这样,怎么办?

我使用 .Net Framework 4.5.2,所以我猜那是 C# 5.0(并且更新目前不是一个选项)。

public enum Product { Product1, Product2, Product3 };
public enum Fund { Fund1, Fund2, Fund3 }
//...and lots of different enums

private static Dictionary<T, double> ConvertDict<T>(Dictionary<string, double> dict) where T : Enum
{
    var returnDict = new Dictionary<T, double>();
    //Here ideally I would do something like this
    switch (typeof(T))
    {
        case typeof(Product): //This a no go
            foreach (var keyValuePar in dict)
                returnDict.Add(CustomProductEnumConverter(keyValuePar.Key), keyValuePar.Value);
            break;
        case typeof(Fund): //This a no go
            foreach (var keyValuePar in dict)
                returnDict.Add(CustomFundEnumConverter(keyValuePar.Key), keyValuePar.Value);
            break;
        default:
            throw new Exception("Unknown enum-type");        
    }
    return returnDict;
}

public static Product CustomProductEnumConverter(string productName)
{
    //No clear link between enum name and string value...
    if (productName == "base012")
        return Product.Coffee;
    if (productName == "defa341")
        return Product.Milk;
    if (productName == "urak451")
         return Product.Juice;
    //...
}

这个想法是我可以像这样调用我的新函数

var prodDictA = ConvertToDict<Product>(rawProdDictA)
var prodDictB = ConvertToDict<Product>(rawProdDictB)
var fundDictA = ConvertToDict<Fund>(rawFundDictA)
//etc...

标签: c#generics

解决方案


您可以通过为密钥注入转换器来解决它;从string到想要的密钥类型T

这是一个例子:

public enum Product { Product1, Product2, Product3 };
public enum Fund { Fund1, Fund2, Fund3 }
//...and lots of different enums

private static Dictionary<T, double> ConvertDict<T>(
    Dictionary<string, double> dict,
    Func<string, T> convertKey)
{
    var returnDict = new Dictionary<T, double>();

    foreach (var kvp in dict)
    {
        returnDict.Add(convertKey(kvp.Key), kvp.Value);
    }

    return returnDict;
}

private static Product ConvertProductName(string productName)
{
    if (productName == "prod1")
        return Product.Product1;
    if (productName == "prod2")
        return Product.Product2;
    if (productName == "prod3")
        return Product.Product3;
    throw new ArgumentException("Unknown product: " + productName);
}

private static Fund ConvertFundName(string fundName)
{
    if (fundName == "fund1")
        return Fund.Fund1;
    if (fundName == "fund2")
        return Fund.Fund2;
    if (fundName == "fund3")
        return Fund.Fund3;
    throw new ArgumentException("Unknown fund: " + fundName);
}

简化:ConvertDict可以使用单个 LINQ 表达式重写该方法,如下所示:

private static Dictionary<T, double> ConvertDict<T>(
    Dictionary<string, double> dict,
    Func<string, T> convertKey)
{
    return dict.ToDictionary(
        kvp => convertKey(kvp.Key),
        kvp => kvp.Value);
}

简化:switch使用语句转换方法会更好:

private static Product ConvertProductName(string productName)
{
    switch (productName)
    {
        case "prod1": return Product.Product1;
        case "prod2": return Product.Product2;
        case "prod3": return Product.Product3;
        default:
            throw new ArgumentException("Unknown product: " + productName);
    }
}

然后你这样称呼它:

    Dictionary<Product, double> prodDictA = ConvertDict<Product>(rawProdDictA, ConvertProductName);
    Dictionary<Product, double> prodDictB = ConvertDict<Product>(rawProdDictB, ConvertProductName);
    Dictionary<Fund, double> fundDictA = ConvertDict<Fund>(rawFundDictA, ConvertFundName);

简化:或者,您甚至可以省略泛型类型,让编译器找出<T>您使用的给定 convertKey 函数:

    Dictionary<Product, double> prodDictA = ConvertDict(rawProdDictA, ConvertProductName);
    Dictionary<Product, double> prodDictB = ConvertDict(rawProdDictB, ConvertProductName);
    Dictionary<Fund, double> fundDictA = ConvertDict(rawFundDictA, ConvertFundName);

推荐阅读