【发布时间】:2018-07-18 09:15:57
【问题描述】:
我不确定我是否需要在这里或https://softwareengineering.stackexchange.com/ 上问这个问题,但让我们从我目前所拥有的开始。
现状
我正在维护一个数据转换器,它正在逐个记录转换,其中很多(30M+)。 我从旧数据库收到一个 id,插入后我有了新的主键。这些键将存储在某种字典中,因此当我需要查找新 ID 时,我可以找到它。 这是通过以下代码完成的(简化的示例代码,不是真实的)
public class PersonConverter : Converter
{
public bool Convert(int oldPersonId /*some more parameters*/)
{
int newPersonId;
try
{
newPersonId = GetNewPersonIdForPerson(oldPersonId);
}
catch (SourceKeyNotFoundException)
{
SomeLogging("Log message");
return true;
}
//lots of more thing are happening here!
return true;
}
}
public class Converter
{
private Dictionary<int,int> _convPersonId;
protected int GetNewPersonIdForPerson(int oldPersonId)
{
int key = oldPersonId;
if (_convPersonId.TryGetValue(key, out int newPersonId))
return newPersonId;
throw new SourceKeyNotFoundException(key.ToString());
}
protected void SomeLogging(string message)
{
//Implement logging etc...
}
}
在PersonConverter 中,我们对GetNewPersonIdForPerson(oldPersonId) 进行了一次调用,但在实际代码中,有很多对不同字典的调用。如您所见,我的前任喜欢抛出异常。性能方面这并不理想,根据微软关于Exceptions & Performance 的网站,他们建议使用Tester-Doer Pattern 或Try-Parse pattern
解决方案
解决方案 1
我想出的解决方案是让GetNewPersonIdForPerson(oldPersonId) 返回int.MinValue 或其他一些确定性值,而不是try/catch block 使用if/else block 检查该值。
抢新方法GetNewPersonIdForPerson2(int oldPersonId)
protected int GetNewPersonIdForPerson2(int oldPersonId)
{
int key = oldPersonId;
if (_convPersonId.TryGetValue(key, out int newPersonId))
return newPersonId;
return int.MinValue;
}
以及我在Convert 的PersonConverter 方法中调用它而不是try/catch block 的方式
if(GetNewPersonIdForPerson2(oldPersonId) != int.MinValue)
{
newPersonId = GetNewPersonIdForPerson(oldPersonId);
}
else
{
SomeLogging("Log message");
return true;
}
这个解决方案有一些问题,因为我需要调用GetNewPersonIdForPerson2 两次事件,尽管我认为性能方面这比抛出异常要快。
解决方案 2
另一种解决方案是在 GetNewPersonIdForPerson 方法上使用 out 变量,如下所示
protected bool GetNewPersonIdForPerson3(int oldPersonId, out int returnPersonId)
{
int key = oldPersonId;
if (_convPersonId.TryGetValue(key, out returnPersonId))
return true;
return false;
}
并在 PersonConverter 的 Convert 方法中执行以下操作
if (!GetNewPersonIdForPerson3(oldPersonId, out newPersonId))
{
SomeLogging("Log message");
return true;
}
我还没有做任何事情来重构这个,因为我想要一些关于什么是最佳解决方案的输入。我更喜欢Solution 1,因为这更容易重构,但在同一个字典中有 2 次查找。我不能确切地说出我有多少Try/Catch blocks,但有很多。 GetNewPersonIdForPerson 方法不是我唯一拥有的(20+)不知道精确的方法。
问题
谁能告诉我解决这个问题的好模式是什么,或者是我想出的最好的两个解决方案之一。
PS: 对于大转换,根据性能计数器# of Exceps Thrown
PS 2: 这只是一些示例代码和与此示例不同,字典永远存在。
【问题讨论】:
-
使用解决方案 #1,您可以将
GetNewPersonIdForPerson2(oldPersonId)放在 if 语句之外的单独变量中,避免执行 2 次。 -
@dlxeon 你说得对,谢谢,没想到那个!但话又说回来,这将是最好的解决方案。
-
版本#2,可以避免很多不必要的工作。
-
@AlessandroD'Andria 你能解释一下为什么我选择解决方案2可以避免很多不必要的工作吗?
-
@JordyvanEijk 为什么,在第一种情况下,我们需要抛出异常?异常应该代表一种异常情况,我们没有预料到会发生这种情况,这不是我们的情况,抛出异常是一项代价高昂的操作。
标签: c# design-patterns exception-handling anti-patterns