我有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(...)
{ ... }
其中object
为string
或HashSet<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);
}
}
更干净!
答案 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;
}