我的进程需要在数据库中创建一堆记录,如果出现任何问题,请将所有内容回滚。我想做的是:
Public Structure Result
Public Success as Boolean
Public Message as String
End Structure
Private _Repository as IEntityRepository
Public Function SaveOrganization( _
ByVal organization As rv_o_Organization) As Result
Dim result = Result.Empty
_Repository.Connection.Open()
_Repository.Transaction = _Repository.Connection.BeginTransaction()
''//Performs validation then saves it to the database
''// using the current transaction
result = SaveMasterOrganization(organization.MasterOrganization)
If (Not result.Success) Then
GoTo somethingBadHappenedButNotAnException
End If
''//Performs validation then saves it to the database
''//using the current transaction
result = SaveOrganziation(dbOrg, organization)
If (Not result.Success) Then GoTo somethingBadHappenedButNotAnException
somethingBadHappenedButNotAnException:
_Repository.Transaction.Commit()
_Repository.Connection.Close()
Return result
End Sub
这是否可以正确使用GoTo声明,或者只是非常糟糕的设计?有更优雅的解决方案吗?希望这个样本能够得到重点
答案 0 :(得分:13)
如果你不得不问,不要这样做。
对于您的特定代码,您可以这样做:
Public Function SaveOrganization(ByVal organization As rv_o_Organization) As Result
Dim result As Result = Result.Empty
_Repository.Connection.Open()
_Repository.Transaction = _Repository.Connection.BeginTransaction()
'Performs validation then saves it to the database
'using the current transaction
result = SaveMasterOrganization(organization.MasterOrganization)
'Performs validation then saves it to the database
'using the current transaction
If result.Success Then result = SaveOrganziation(dbOrg, organization)
_Repository.Transaction.Commit()
_Repository.Connection.Close()
Return result
End Sub
答案 1 :(得分:6)
设计真糟糕。是。
答案 2 :(得分:6)
Goto声名狼借,它会让其他开发人员立刻认为你的代码很糟糕。即使你可以证明使用goto是最好的设计选择 - 你必须一次又一次地向任何看到你代码的人解释它。
为了您自己的声誉,请不要这样做。
答案 3 :(得分:2)
可能存在一些适用的极端边缘情况,但几乎毫不含糊,不,不使用它。
在这种特定情况下,您应该使用Using语句以更好的方式处理它。通常,您将创建一个实现IDisposable的类(或使用已经执行过的ID),然后在Dispose方法中处理清理。在这种情况下,您将关闭与数据库的连接(显然,它会从您的设计中重新打开)。
另外,我建议在这里使用TransactionScope类,您可以使用它来调整事务范围然后提交它,以及在异常情况下自动中止。
答案 4 :(得分:2)
唯一一次你应该使用goto,就是没有其他选择。
找出是否没有其他选择的唯一方法就是全部尝试。
在你的特定例子中,你应该使用try ... finally,就像这样(抱歉,我只知道C#)
void DoStuff()
{
Connection connection = new Connection();
try
{
connection.Open()
if( SomethingBadHappened )
return;
}
finally
{
connection.Close();
}
}
答案 5 :(得分:1)
我会非常谨慎地说。每当我不得不考虑使用GOTO语句时,我会尝试重构代码。我能想到的唯一例外是在vb中使用语句On Error Goto。
答案 6 :(得分:1)
goto没有任何固有的错误,但这不是一个非常理想的用法。我认为你对异常的定义提出了过分的要求。
只需抛出一个自定义异常并将回滚代码放在那里。我认为如果发生真正的异常,你也会想要回滚,所以你也可以从中获得双重责任。
答案 7 :(得分:1)
Gotos只是一个实现细节。 try / catch很像一个goto(一个堆栈之间的goto!)如果你想要的话,while循环(或任何构造)可以用gotos编写。中断和早期返回语句是他们所有人中最薄弱的伪装 - 它们是公然的(有些人因为相似性而不喜欢它们)
所以从技术上来说,没有什么东西真的与它们有关,但它们确实会造成更难的代码。当您使用循环结构时,您将绑定到大括号区域。没有想知道你实际去哪里,搜寻或纵横交错。
最重要的是,他们有一个非常糟糕的代表。如果你决定使用一个,即使是在最好的情况下,你也必须为所有读过你代码的人辩护你的决定 - 许多你将要防御的人将没有能力让判断自己打电话,所以你鼓励不好的代码。
你的情况的一个解决方案可能是使用早期返回与goto相同的事实(ps。有史以来最糟糕的伪代码):
dbMethod() {
start transaction
if(doWriteWorks())
end Transaction success
else
rollback transaction
}
doWriteWorks() {
validate crap
try Write crap
if Fail
return false
validate other crap
try Write other crap
if Fail
return false
return true
}
我认为这种模式可以在VB中运行,但我从VB 3开始就没用过它(大概是MS买的时候),所以如果事务以某种方式绑定到执行方法上下文或其他东西,那么我就不知道了。我知道MS倾向于将数据库与代码结构紧密绑定,否则我甚至不会考虑这种方法不起作用的可能性......
答案 8 :(得分:1)
我在特定地方一直使用goto,例如在Try Catch正上方,以防你提示用户“Retry?,Cancel”,如果重试然后转到StartMyTask:并增加Current Try of Maximum retries视情况而定。
对于每个循环,它们也很方便。
For each Items in MyList
If VaidationCheck1(Item) = false then goto SkipLine
If ValidationCheck(Item) = false then goto skipline
'Do some logic here, that can be avoided by skipping it to get better performance.
'I use then like short circuit operands, why evaluate more than you actually have to?
SkipLine:
Next
我不会用它们代替函数来制作非常庞大的长代码块,只是在他们可以真正帮助的小地方,主要是为了跳过这些东西。
答案 9 :(得分:0)
每当我看到使用goto时,简单的重构都可以处理它。我建议永远不要使用它,除非你“知道”你必须使用它
答案 10 :(得分:0)
我很想说永远不会,但我想总有一种情况可能是最好的解决方案。但是,我在没有使用Goto声明的情况下编程了大约20年左右,并且无法预见很快就会需要。
答案 11 :(得分:0)
为什么不在try catch块中包装每个函数调用,当完成后,如果抛出了一个异常,就可以捕获它并关闭连接。这样,您完全避免使用GOTO语句。
简而言之,GOTO声明并不是一件好事,除非在异常情况下,即使这样,通常也需要重构来避免它。不要忘记,它是早期语言的遗留物,在本例中是BASIC。
答案 12 :(得分:0)
我会说非常谨慎使用,因为它通常与引入spagetti代码有关。尝试使用方法而不是标签。
我认为使用GOTO的一个好例子是创建一个可用于C#而不是VB的select流程。
答案 13 :(得分:0)
go to statement倾向于使程序流程难以理解。我不记得在过去的十年中使用它,除了在visual basic 6和“on error”的组合中。
就我而言,你对go的使用是可以接受的,因为程序流程非常清楚。我不认为使用try ... catch会改进很多东西,因为你需要在现在的go tos上放置异常。
然而,格式化并不是很吸引人: - )
我会将go to label的名称更改为其他名称,因为当一切都成功时也会到达此位置。 clean_up:会很好。
答案 14 :(得分:0)
呃,在VBscript / ASP中有一个很好用的处理错误。我们使用它将错误处理返回到ASP,一旦我们完成了使用错误恢复。
<。>在.net?天哪,不!答案 15 :(得分:0)
每个人都说避免它,但为什么。 GOTO语法是汇编中的跳转语句 - 非常有效。
避免它的主要原因是代码可读性。你需要在代码中找到难以观察的GOTO标签。
有些人认为它可能会导致内存泄漏但我已经看到专家说在.NET中不是这样。