【问题标题】:Is it possible to create a method that returns one of two possible types?是否可以创建一个返回两种可能类型之一的方法?
【发布时间】:2013-02-19 18:13:55
【问题描述】:

我有 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&lt;string&gt;。但是我从Dictionary&lt;string, object&gt; 转换到Multimap&lt;string, string&gt; 时运气不佳。

从方法中提取逻辑是一种选择,但这不是很好。由于 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&lt;TKey, TValue&gt; 有什么问题?这是可能的,因为 Multimap 扩展了 Dictionary。 Multimap 中是否有您需要的特别之处?如有必要,您可以返回字典,然后尝试转换为 Multimap
  • 也许是字典?

标签: c# generics dictionary casting


【解决方案1】:

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

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

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

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

【讨论】:

  • T 不是 IDictionary 在 MultiMap 的情况下
  • @JustinPihony 添加了一种不同的方法来处理这种可能性。
  • 嗯,我正在考虑我是否喜欢这个...如果您删除第一个选项,可能 +1,因为我们知道它们不共享相同的泛型类型
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • 事情在MultiMap方法中,你希望新的Add(TKey, TValue)方法被调用,而如果它继承自Dictionary&lt;TKey, HashSet&lt;TValue&gt;&gt;显式实现IDictionary.Add,你将调用继承Add(TKey, HashSet&lt;TValue&gt;) 方法代替。
    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

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

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

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

      ETA:假设 MultiMap 实现了IDictionary&lt;K,V&gt;。既然你说它继承自Dictionary&lt;K,V&gt;,我就认为它确实如此。

      【讨论】:

        【解决方案6】:

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

        public T Populate<T>(string val) where T : Dictionary<string, string>, new()
                {
                    T returnDict = new T();
                    returnDict.Add("key", "val");
                    return returnDict;
                }
        

        【讨论】:

        • 在他的例子中就是这样,所以这就是每个人都引用它的原因。如果他想使用不同的类型,那么他可以在方法签名中指定。
        • @Eluvatar 不是,它是一个Dictionary&lt;string, HashSet&lt;string&gt;&gt;碰巧有一个额外的Add(string, string) 方法。
        猜你喜欢
        • 1970-01-01
        • 2014-06-22
        • 2012-12-02
        • 1970-01-01
        • 2023-03-17
        • 1970-01-01
        • 2014-08-30
        • 1970-01-01
        • 2020-01-27
        相关资源
        最近更新 更多