有没有可能创建一个返回两种可能类型之一的方法?

4

我有两个数据结构:Dictionary<string, string>Multimap<string, string>。 Multimap 其实就是在底层使用了一个 Dictionary。我借鉴了这个问题上的大部分代码。以下是类的定义:

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可以是stringHashSet<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);
        }
    }

更加整洁!

返回 Dictionary<TKey, TValue> 有什么问题吗?这是可能的,因为 Multimap 扩展了 Dictionary。Multimap 中是否有特殊需求?如果必要,您可以返回一个字典,然后尝试将其转换为 Multimap - Dave Zych
也许是 Dictionary<string, dynamic>? - It'sNotALie.
6个回答

8
为了解决缺少公共接口的问题,可以使用一系列委托类型参数自行发明一个临时接口:
void MethodThatDoesAllTheLogic(Action<string, string> addFunc)
{
    // ...
    addFunc(key, value);
    // ...
}

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

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

在MultiMap的情况下,T不是IDictionary<string,string>。 - Justin Pihony
@JustinPihony 提供了一种不同的方法来处理这种可能性。 - millimoose
嗯,我正在考虑是否喜欢这个...如果您删除第一个选项,可能会得到+1,因为我们知道它们不共享相同的通用类型。 - Justin Pihony

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

0

如果你只是想要一个Add方法,那么两个对象应该共享IDictionary。然而,那个Add方法只使用对象。这可能是在不必使用泛型的情况下最接近的方法...但是在那一点上你再次失去了泛型的好处。


问题在于MultiMap方法中,您希望调用新的Add(TKey, TValue)方法,但如果它继承自Dictionary<TKey, HashSet<TValue>>并且显式实现了IDictionary.Add,则会调用继承的Add(TKey, HashSet<TValue>)方法。 - millimoose

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代理是否适合您的问题。请尝试提供更多有关它的信息。


-1
如果您的内部逻辑除了 TValue 类型之外完全相同 - 我的意思是逐字逐句完全相同 - 那么您可以这样做:
IDictionary<string, TValue> MethodThatDoesAllTheLogic<TValue>(whatever)
{
  // word for word-identical logic
}

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

预计时间:这假定MultiMap实现了IDictionary<K,V>。由于您说它继承自Dictionary<K,V>,所以我假设它确实如此。


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

在他的例子中,这就是为什么每个人都在引用它。如果他想使用不同的类型,那么他可以在方法签名中指定。 - Eluvatar
1
@Eluvatar,这不是一个额外的Add(string, string)方法,而是一个Dictionary<string, HashSet<string>>,只是恰好有这个方法。 - millimoose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接