过早的泛化会导致过早优化成为罪魁祸首。泛化是昂贵的,昂贵的代码应该有明确的收益来证明其正当性。因此,我建议基于特定场景增加你的方法的普适性。我可以想到许多方法来使你的方法更加普适:
- 不要使用字符串到字符串的字典;使用字符串到任意类型的字典。
- 不要使用Dictionary<...>;使用IDictionary<...>。
- 不要使用任何类型的字典。使用一个Action,它的操作可以将项目输入字典中。
- 不要使用文件名。你只是将文件名转换成流,所以最好直接使用Stream。让调用者为你打开流。
- 不要使用Stream。你只需要将流转换成一系列的项,所以最好使用IEnumerable<...>,并让调用者为你解析流。
我们能使这个方法变得多么通用呢?假设我们有一系列T,然后将其转换为从K到V的映射。我们需要什么?一个序列、一个键提取器、一个值提取器和一个映射编写器。
static void MakeMap<T, K, V>(this IEnumerable<T> sequence, Action<K, V> mapWriter, Func<T, K> keyExtractor, Func<T, V> valueExtractor)
{
foreach(var item in sequence)
mapWriter(keyExtractor(item), valueExtractor(item));
}
注意当代码变得非常通用时,它会变得非常简短。这非常普遍,因此调用站点现在看起来像一团混乱。那真的是你想要的吗?你会有效地使用所有这些普遍性吗?可能不会。你实际上有哪些场景驱使你希望更加通用?使用这些具体场景来激励你的重构。
我们能走得比这更远吗?当然可以。例如,我们可以注意到在其中具有副作用似乎很糟糕。该方法是否应该只返回一个新构建的字典?该字典的比较策略应该是什么?如果有多个具有相同键的项目怎么办?应该替换还是应该让字典实际上成为从键到值列表的映射?我们可以采取这些想法并创建一个解决所有这些问题的新方法:
public static ILookup<TKey, TElement> ToLookup<TSource, TKey, TElement>(
this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector,
Func<TSource, TElement> elementSelector,
IEqualityComparer<TKey> comparer) { ... }
嗨,我们刚刚重新发明了ToLookup()扩展方法。有时候当你把一个方法变得足够通用时,你会发现它已经存在。