是否可以创建一个返回两种可能类型之一的方法?

时间:2013-02-19 18:13:55

标签: c# generics dictionary casting

我有2个数据结构:Dictionary<string, string>Multimap<string, string>。 Multimap实际上只是一个字典。我从this question获取了必须的代码。这是类定义:

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(...)
{ ... }

其中objectstringHashSet<string>。但是我从Dictionary<string, object>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);
        }
    }

更干净!

6 个答案:

答案 0 :(得分:8)

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

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

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

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

答案 1 :(得分:3)

我会避免让helper方法创建实际的集合;让它只填充现有的集合。这可以更有效地完成,因为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;
}

答案 2 :(得分:0)

如果您只是在寻找Add方法,那么两个对象都应该共享IDictionary。但是,Add方法仅使用对象。这可能是你无需在方法中使用泛型就可以得到的最接近的......但是在这一点上你再次失去了泛型的好处。

答案 3 :(得分:0)

看看这种方法是否有用: 关键是在创建对象(Dictionary或Multimap)和获取值时进行抽象 - 填充方法中的两个不同。

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委托是否适合您的问题。尝试提供更多相关信息。

答案 4 :(得分:-1)

如果你的内在逻辑是真正相同的,除了TValue是什么类型 - 我的意思是逐字逐句 - 那么你可以做类似的事情:

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

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

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

答案 5 :(得分:-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;
        }