我有这个名为LoadToDictionary的方法。它接受字典和文件路径。目前,它看起来像这样:
void LoadToDictionary(Dictionary<string, string> dict, string filePath
。然后在里面,它会有很多代码将filePath中文件的内容提取到字典中。我想让它更通用,所以它可以,比如说,用一个带有int的字典,以及改变方法中的解析代码的能力。我应该标记此方法,然后覆盖它吗?还有其他办法吗?
答案 0 :(得分:36)
过早的普遍性使得过早的优化成为金钱的根源。 通用性很高,昂贵的代码应该通过明确的好处来证明。
因此,我会在特定场景的基础上提高您的方法的通用性。我可以想出许多方法来使你的方法更通用:
不要把字符串字典串起来;把字符串比作任意类型。
不要带Dictionary<...>
;采取IDictionary<...>
。
不要拿任何字典。取一个Action<K, V>
,其行动可能是在字典中输入该项目。
不要使用文件名。无论如何,您只是将文件名转换为流,因此请先开始使用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()扩展方法。有时,当你使方法足够通用时,你会发现它已经存在。
答案 1 :(得分:1)
我在这里看到两个实际问题:
1)方法签名可以是动态的,以便它所期望的类型可以改变吗?
2)方法体可以是动态的,这样我的逻辑就可以在运行中改变。
我不确定如何这两个中的任何一个都可以通过使用Reflection和相当多的代码来完成。不过,我确信这里有人。
根据您的需要,基本方法重载听起来就像您实际需要的那样。
使用您的示例:
void LoadToDictionary(Dictionary<string, string> dict, string filePath)
{ ... code here ... }
//to have a LoadToDictionary method which accepts Dictionary<int, int>
void LoadToDictionary(Dictionary<int, int> dict, string filePath)
//To change the processing logic, you either need a new method name,
// or to override the original in an inheriting class
void AlternateLoadToDictionary(Dictionary<string, string> dict, string filePath)
override void LoadToDictionary(Dictionary<string, string> dict, string filePath)
更好的选择是将解析逻辑与Dictionary创建逻辑分开。您甚至可以从LoadDictionary
方法中调用其他方法,这样正常的重载也可以在那里工作
void LoadToDictionary(Dictionary<string, string> dict, string filePath)
{
string[] fileLines;
if([yourBaseLineCondition])
fileLines = LoadDataFromFile(filePath, false);
else fileLines = LoadDataFromFile(filePath, true);
}
string[] LoadDataFromFile(string filePath, bool altLogic)
{
if(altLogic) return LoadDataFromFileAlt(filePath)
else
{ ... your logic ... }
}
string[] LoadDataFromFileAlt(string filePath)
{ ... your alt logic... }
基本方法重载为您提供了很多灵活性。更重要的是,它允许您遵循YAGNI原则而不会切断扩展途径。如果需要,您可以随时返回代码并添加LoadToDictionary<myCustomObject, myCustomObject>
方法。