另一种不使用 GOTO 关键字的重试逻辑方式
实现中止/重试/忽略逻辑的另一种方法
引言
上面的对话框应该是我们日常操作中一个很常见的错误提示。
如果后面的操作只是一个简单的动作,比如复制一个文件,那么实现重试逻辑也会很简单。
但是这篇文章将讨论在顺序动作中实现重试逻辑。例如,复制 10 个文件,第 5 个文件复制失败,并显示一个消息框,现在用户应该决定 '中止'、'重试' 或 '忽略'。并且基于用户的响应,我们现在讨论如何实现“中止”、“重试”和“忽略”逻辑。
背景
最近在安装一些软件的时候看到了一个这样的文件复制错误对话框,带有重试按钮。突然开始思考这应该如何以一种好的方式实现。众所周知,使用“GOTO”关键字进行重试是一种常见且快速的方法,但这可能会弄乱你的逻辑和代码。所以我试图避免使用“GOTO”,而是通过递归方法。
实现逻辑
让我先展示我的演示代码,它实现了“中止”、“重试”和“忽略”逻辑
/// <summary>
/// This is the main function to do the work with retry logic
/// </summary>
/// <param name="jobName">Indicates current job name for displaying in message box</param>
/// <param name="retryCount">Indicates how many retried has been executed</param>
/// <returns>
/// -1: Abort is selected by user in message box
/// 0: Ignore is selected by user in message box or there is no error when doing current job
/// </returns>
private int StartWorkProcess(string jobName, int retryCount)
{
try
{
//do something here
Console.WriteLine("doing job " + jobName + "...");
//demo an exception when the job name's length equals to 3, 6 and 9
if (Math.IEEERemainder(jobName.Length, 3)==0)
{
throw new Exception();
}
}
catch
{
switch (MessageBox.Show("Failed doing job " + jobName, string.Empty, MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error))
{
case System.Windows.Forms.DialogResult.Abort:
return -1; //Abort all
break;
case System.Windows.Forms.DialogResult.Retry:
if (retryCount > 10) //Limite the retry times
{
switch (MessageBox.Show("Too many retries! Yes to abort the whole job, No to ignore current task.", string.Empty, MessageBoxButtons.YesNo, MessageBoxIcon.Warning))
{
case System.Windows.Forms.DialogResult.Yes:
return -1; //Abort all
break;
case System.Windows.Forms.DialogResult.No:
//do nothing more
return 0;
break;
}
}
else
{
return StartWorkProcess(jobName, retryCount + 1);
}
break;
case System.Windows.Forms.DialogResult.Ignore:
//do nothing more
break;
}
}
return 0;
}
这是使用“中止”、“重试”和“忽略”逻辑完成工作的主要方法。
这里有一些关键点
- 使用 try…catch 块来捕获任何异常。当然,您可以将其扩展到多个 catch 以指定某些异常。
- 当出现异常,并且用户选择重试时,此方法会增加 retryCount 并再次调用自身以重做相同的逻辑进行重试。
- 返回值是必要的,以指示状态,并供调用者函数执行自己的工作。
- 重试次数也需要被跟踪,并在重试次数超过阈值时强制中止或忽略当前任务。这可以避免潜在的内存溢出和堆栈溢出问题。
以下是调用者函数,它将使用工作方法的返回值
/// <summary>
/// On OK button click, it will call the work function to do the job.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnOK_Click(object sender, EventArgs e)
{
string job = string.Empty;
//Simulate 9 jobs with job name 1, 12, 123, 1234 and etc.
for (int i = 1; i < 10; i++)
{
job = job + i.ToString();
if (StartWorkProcess(job, 0) == -1)
{
break;
}
}
}
运行代码时,您可以通过 Visual Studio 输出窗口中的程序输出来监视结果。这里显示一个示例输出
doing job 1...
doing job 12...
doing job 123...
A first chance exception of type 'System.Exception' occurred in RetryIgnoreInRecursion.exe
doing job 123...
doing job 123...
A first chance exception of type 'System.Exception' occurred in RetryIgnoreInRecursion.exe
A first chance exception of type 'System.Exception' occurred in RetryIgnoreInRecursion.exe
doing job 1234...
doing job 12345...
doing job 123456...
doing job 1234567...
doing job 12345678...
doing job 123456789...
现在做得更好
现在您应该对我在谈论的内容以及代码中如何实现逻辑有一个基本的了解。但是让我们做得更好,使用模板方法模式使代码可重用。
创建了一个抽象类并实现了基本的重试/忽略/中止逻辑,而实际的工作逻辑尚未实现,而是将其留给子类作为抽象方法。
abstract class RetryIgnoreHandlerBase
{
/// <summary>
/// Abstract method for subclass to implement actual working logic
/// </summary>
protected abstract void DoActualJob(string jobName);
/// <summary>
/// This is the main function to do the work with retry logic
/// </summary>
/// <param name="jobName">Indicates current job name for displaying in message box</param>
/// <param name="retryCount">Indicates how many retried has been executed</param>
/// <returns>
/// -1: Abort is selected by user in message box
/// 0: Ignore is selected by user in message box or there is no error when doing current job
/// </returns>
public int StartWorkProcess(string jobName, int retryCount)
{
try
{
DoActualJob(jobName);
}
catch
{
switch (MessageBox.Show("Failed doing job " + jobName, string.Empty, MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Error))
{
case System.Windows.Forms.DialogResult.Abort:
return -1; //Abort all
break;
case System.Windows.Forms.DialogResult.Retry:
if (retryCount > 10) //Limite the retry times
{
switch (MessageBox.Show("Too many retries! Yes to abort the whole job, No to ignore current task.", string.Empty, MessageBoxButtons.YesNo, MessageBoxIcon.Warning))
{
case System.Windows.Forms.DialogResult.Yes:
return -1; //Abort all
break;
case System.Windows.Forms.DialogResult.No:
//do nothing more
return 0;
break;
}
}
else
{
return StartWorkProcess(jobName, retryCount + 1);
}
break;
case System.Windows.Forms.DialogResult.Ignore:
//do nothing more
break;
}
}
return 0;
}
}
子类继承上述抽象类,并实现实际的工作逻辑
class DemoRetry: RetryIgnoreHandlerBase
{
protected override void DoActualJob(string jobName)
{
//do something here
Console.WriteLine("doing job " + jobName + "...");
//demo an exception when the job name's length equals to 3, 6 and 9
if (Math.IEEERemainder(jobName.Length, 3) == 0)
{
throw new Exception();
}
}
}
然后对调用者函数的相关更改
private void btnOK_Click(object sender, EventArgs e)
{
string job = string.Empty;
DemoRetry demo = new DemoRetry();
//Simulate 9 jobs with job name 1, 12, 123, 1234 and etc.
for (int i = 1; i < 10; i++)
{
job = job + i.ToString();
if (demo.StartWorkProcess(job, 0) == -1)
{
break;
}
}
}
欢迎您提出解决方案
是否有其他解决方案可以实现中止/重试/忽略逻辑?请随时留下评论并与大家分享。
历史
2014 年 4 月,版本 1。