【问题标题】:Convert dictionary, with string key, to dictionary with enum key using Generics使用泛型将带有字符串键的字典转换为带有枚举键的字典
【发布时间】:2020-05-19 19:02:32
【问题描述】:

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

我希望能为所有枚举类型编写一个通用方法,而不是为每个枚举类型编写一个转换函数。

我对泛型(或类型检查)不太熟悉,但下面的代码是编写函数的尝试。 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...

【问题讨论】:

  • 通常在泛型方法中使用typeof 意味着您在设计方面做错了一些事情。枚举转换器长什么样子?
  • @Pavel Anikhouski 也许......在这种情况下,您将返回泛型类型并且只有一次转换。在我的情况下,您必须根据推断的类型进行多次转换。我会看看我是否可以做类似的事情。

标签: c# generics


【解决方案1】:

您可以通过为密钥注入转换器来解决它;从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);

简化:或者,您甚至可以省略泛型类型,让编译器根据您使用的 convertKey 函数计算出 &lt;T&gt;

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

【讨论】:

  • 啊哈!我会试试这个。谢谢。
【解决方案2】:

泛型的意义在于以相同的方式处理所有类型。在您的情况下,客户不会期望以下调用在运行时失败:

var u = ConvertToDict<MyUnrelatedType>(rawProdDictA)

因为泛型参数没有限制。换句话说:当你需要打开类型时,这里没有太多的generic,是吗?

除此之外,让我们在这里看看客户端。当您想调用该方法时,您必须提供通用类型参数:

var prodDictA = ConvertToDict<Product>(rawProdDictA)

那么,这样做的好处在哪里?

var prodDictA = ConvertProductToDict(rawProdDictA)

这使得该方法的目的非常明确。因此,从客户端的角度来看,没有任何好处,因为无论如何您都必须知道 T 的类型。

让我们看看服务器端(方法内部的透视)。在它里面,我们有三个完全不同的分支,它们没有涵盖任何通用的东西。所以不要假装你的方法真的适用于 any 类型,只需创建三个方法:

private static Dictionary<Product, double> ConvertProductToDict(Dictionary<string, double> dict)
{
    var returnDict = new Dictionary<Product, double>();
    foreach(var keyValuePar in dict)
        returnDict.Add(CustomProductEnumConverter(keyValuePar.Key), keyValuePar.Value);
    return returnDict;
}

【讨论】:

  • 感谢您的回答。首先,据我了解,可以对 T 的类型添加限制,这在这里是有意义的。其次,该方法的重点是,首先以一种可以处理的方式包装无法平等处理的东西。并限制转换方法的数量(基本上都是一样的)。显然,您必须在某个时候知道类型,即使在使用泛型(即 List)时也经常会这样做。正如我在帖子中所说,我对这个主题不太熟悉,这就是我问的原因。
【解决方案3】:

如果我是你,我会实现这样的:


using System;
using System.Collections.Generic;
using System.Linq;

public enum Product { Milk, Coffee, Juice };
public enum Fund { Fund1, Fund2, Fund3 };
//...and lots of different enums

public class Program
{
    public static void Main()
    {
        Console.WriteLine("Hello World");

        //Assume that this dictionary contains your elements
        var dict = new Dictionary<string, double>();

        Dictionary<Product, double> result = dict.ConvertDict(CustomProductEnumConverter);

    }

    private 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;
        //implement your own logic
        else throw new Exception();
    }
}

public static class DictionaryExtensions 
{
    public static Dictionary<T, double> ConvertDict<T>(this Dictionary<string, double> dict, Func<string, T> converter)
    {
        var returnDict = new Dictionary<T, double>();
        foreach (var keyValuePar in dict)
            returnDict.Add(converter(keyValuePar.Key), keyValuePar.Value);
        return returnDict;
    }   
}


【讨论】:

    猜你喜欢
    • 2010-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-11
    • 1970-01-01
    • 2021-12-09
    • 2020-07-21
    • 2023-02-09
    相关资源
    最近更新 更多