4

我有 2 个数据结构:Dictionary<string, string>Multimap<string, string>. Multimap 实际上只是引擎盖下的字典。我从这个问题中提取了必须的代码。这是类定义:

public class Multimap<TKey, TValue> : Dictionary<TKey, HashSet<TValue>>
{ ... }

两种数据结构都有一个.Add(TKey key, TValue value)方法。

我有一个类负责从某些文件中填充这些地图。我目前有以下两种方法:

    public Dictionary<string, string> PopulateDictionary(...)
    {
        Dictionary<string, string> returnDictionary = new Dictionary<string, string>();
        ...
        foreach (...)
        {
            ...
            returnDictionary.Add(key, value);
        }
        return returnDictionary;
    }

    public Multimap<string, string> PopulateMultimap(...)
    {
        Multimap<string, string> returnMultimap = new Multimap<string, string>();
        ...
        foreach (...)
        {
            ...
            returnMultimap.Add(key, value);
        }
        return returnMultimap;
    }

如您所见,它们完全相同,都大约 25 行长,唯一的区别是它们的返回类型。我要做的就是将其浓缩为一种方法。我的第一次尝试是拥有该方法

public Dictionary<string, object> PopulateGenericDictionary(...)
{ ... }

要么object要么。string_ HashSet<string>但是我没有太多运气从Dictionary<string, object>to 转换Multimap<string, string>

从方法中提取逻辑是一种选择,但这不是很好。由于 foreach 循环,这两种方法中总会有一些逻辑。你最终会得到两倍小的方法,但仍然有两种相同的方法,这并不能真正解决问题。

这将是我理想的方法结构:

public Dictionary<string, string> PopulateDictionary(...)
{
    return MethodThatDoesAllTheLogic(...);
}
public Multimap<string, string> PopulateMultimap(...)
{
    return MethodThatDoesAllTheLogic(...);
}
public ??? MethodThatDoesAllTheLogic(...)
{ ... }

我一直在摆弄铸造和泛型,但我就是无法让它工作。有任何想法吗?

编辑

我使用了millimoose的解决方案。这是我现在的代码:

    public Dictionary<string, string> GenerateDictionary(...)
    {
        Dictionary<string, string> returnMap = new Dictionary<string, string>();
        PopulateDictionary(returnMap.Add, ...);
        return returnMap;
    }

    public Multimap<string, string> GenerateMultimap(...)
    {
        Multimap<string, string> returnMap = new Multimap<string, string>();
        PopulateDictionary(returnMap.Add, ...);
        return returnMap;
    }

    private static void PopulateGenericDictionary(Action<string, string> addFunc, ...)
    {
        ...
        foreach (...)
        {
            addFunc(key, value);
        }
    }

干净多了!

4

6 回答 6

8

要解决缺少通用接口的问题,您可以使用一堆委托类型参数来创建一个临时接口:

void MethodThatDoesAllTheLogic(Action<string, string> addFunc)
{
    // ...
    addFunc(key, value);
    // ...
}

public Dictionary<...> PopulateDictionary()
{
    // ...
    MethodThatDoesAllTheLogic(result.Add);
}

(根据需要添加更多参数。)

于 2013-02-19T18:19:34.133 回答
3

我会避免让辅助方法创建实际的集合;让它只是填充现有的集合。这可以更有效地完成,因为该Add方法在两种情况下都具有相同的签名。我们可以只使用委托来接受该Add方法:

public static void PopulateMapping<TKey, TValue>(Action<TKey, TValue> addMethod,
    IEnumerable<TKey> data) //include other parameters needed to populate the data
{
    foreach (var key in data)
    {
        addMethod(key, default(TValue));
    }
}

然后它将像这样使用:

public static Dictionary<string, string> PopulateDictionary()
{
    Dictionary<string, string> output = new Dictionary<string, string>();
    PopulateMapping<string, string>(output.Add, new string[] { "a" });
    return output;
}
于 2013-02-19T18:26:47.007 回答
0

如果您只是在寻找 Add 方法,那么两个对象应该共享IDictionary. 但是,该Add方法仅使用对象。这可能是您无需在方法中使用泛型即可获得的最接近的方法……但此时您再次失去了泛型的好处。

于 2013-02-19T18:21:34.417 回答
0

看看这种方法是否有用:关键是对对象(字典或多图)的创建和获取值进行抽象——填充方法的两个区别。

public  Dictionary<string, TValue> Populate<TValue>( Dictionary<string, TValue> returnDict, Func<SomeType, TValue> valueProvider)
{
    string key = null;
    ...
    foreach (...)
    {
        ...
        returnDict.Add(key, valueProvider(value));
    }
    return returnDict;
}

示例调用可以是:

public void Test()
{
    Populate(new Multimap<string, HashSet<string>>(), (t) => new HashSet<HashSet<string>>());
}

我不确定 valueProvider 委托是否适合您的问题。尝试提供更多有关它的信息。

于 2013-02-19T18:40:47.187 回答
-1

如果除了 TValue 的类型之外,您的内部逻辑确实是相同的-我的意思是逐字逐句相同-那么您可以执行以下操作:

IDictionary<string, TValue> MethodThatDoesAllTheLogic<TValue>(whatever)
{
  // word for word-identical logic
}

我使该方法将 TValue 作为其唯一的类型参数,因为这是唯一的区别(在您展示的示例中):两种方法都将字符串作为第一个类型参数。

ETA:这假设 MultiMap 实现了IDictionary<K,V>. 既然你说它继承Dictionary<K,V>自我认为它确实如此。

于 2013-02-19T18:19:39.690 回答
-1

在具有泛型的 C# 中,您可以要求它们在我们的案例 Dictionary 中扩展或实现特定类,以下是您可能实现的方法。

public T Populate<T>(string val) where T : Dictionary<string, string>, new()
        {
            T returnDict = new T();
            returnDict.Add("key", "val");
            return returnDict;
        }
于 2013-02-19T18:23:44.227 回答