简体   繁体   English

使用 Generics 将带有字符串键的字典转换为带有枚举键的字典

[英]Convert dictionary, with string key, to dictionary with enum key using Generics

I have a bunch of dictionaries (with string as key) that I read from an external source.我有一堆从外部来源读取的字典(以字符串为键)。 I would like to convert all the dictionaries to dictionaries with different enums as keys.我想将所有字典转换为具有不同枚举作为键的字典。 For each enum, I have a custom method that converts the string to an enum.对于每个枚举,我都有一个将字符串转换为枚举的自定义方法。

Rather than writing one convert-function for each enum type, I was hoping I could write one generic method for all.与其为每种枚举类型编写一个转换函数,我希望我可以为所有类型编写一个通用方法。

I'm not really that familiar with generics (or type-checking for that matter), but the below code is an attempt at writing the function.我不太熟悉 generics(或类型检查),但下面的代码是尝试编写 function。 The vs-intellisense does not like this solution. vs-intellisense 不喜欢这种解决方案。 Is it possible to do what I'm trying to do?有可能做我想做的事吗? And if so, how?如果是这样,怎么办?

I use.Net Framework 4.5.2 so I guess that is C# 5.0 (and updating is currently not an option).我使用.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;
    //...
}

The idea is that I would be able to call my new function like this这个想法是我可以像这样调用我的新 function

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

You can solve it by injecting the converter for the key;您可以通过为密钥注入转换器来解决它; from string to the wanted key type T .string到想要的键类型T

Here's an example:这是一个例子:

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);
}

Simplification: The ConvertDict method can be rewritten using a single LINQ-expression like this:简化:可以使用单个 LINQ 表达式重写ConvertDict方法,如下所示:

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);
}

Simplification: And the convert methods would be nicer using a switch statement:简化:使用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);
    }
}

Then you call it this way:然后你这样称呼它:

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

Simplification: Or, you can even leave out the generic type and let the compiler figure out the <T> given the convertKey func you use:简化:或者,您甚至可以省略泛型类型,让编译器根据您使用的 convertKey 函数计算出<T>

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

The point of generics is to handle all types the same way. generics 的要点是以相同的方式处理所有类型。 In your case clients won´t expect the following call to fail at runtime:在您的情况下,客户不会期望以下调用在运行时失败:

var u = ConvertToDict<MyUnrelatedType>(rawProdDictA)

because there´s no restriction on the generic argument.因为对泛型参数没有限制。 In other words:w hen you need to switch on the type, there´s not much generic here, is it?换句话说:当你需要打开类型时,这里没有太多的泛型,是吗?

Apart from this let´s see the client-side here.除此之外,让我们在这里看看客户端。 When you want to call the method, you´d have to provide the generic type-parameter:当您想调用该方法时,您必须提供通用类型参数:

var prodDictA = ConvertToDict<Product>(rawProdDictA)

So where is the benefit over this?那么这样做的好处在哪里呢?

var prodDictA = ConvertProductToDict(rawProdDictA)

This makes the purpose of the method pretty clear.这使得该方法的目的非常明确。 So from the client-view there´s no benefit, as you´d have to know the type T anyway.因此,从客户端的角度来看,没有任何好处,因为无论如何您都必须知道类型T

Let´s see the server-side (the persepctive from inside your method).让我们看看服务器端(方法内部的透视)。 Inside it we have three completely distinct branches that cover nothing generic.在其中,我们有三个完全不同的分支,它们没有涵盖任何通用的内容。 So instead of pretending your method really works for any type just create three methods:所以不要假装你的方法真的适用于任何类型,只需创建三个方法:

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;
}

If I were you, I'd implement something like that:如果我是你,我会实现类似的东西:


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;
    }   
}


声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM