在这种情况下,有比GOTO更好的方法吗?

本文关键字:方法 更好 GOTO 这种情况下 有比 | 更新日期: 2023-09-27 18:03:15

让我们诚实地开始吧。我不是goto的粉丝,但我也不喜欢一遍又一遍地重复我的代码。不管怎样,我碰到了这个场景。这是不寻常的,但也不是独一无二的。我不禁想知道这对goto来说是不是一个好情况。有没有更好的办法?

public Task Process(CancellationTokenSource token)
{
    await SpinUpServiceAsync();
    foreach (var item in LongList())
    {
        if (token.IsCancellationRequested) goto cancel;
        await LongTask1(item);
        if (token.IsCancellationRequested) goto cancel;
        await LongTask2(item);
        if (token.IsCancellationRequested) goto cancel;
        await LongTask3(item);
        if (token.IsCancellationRequested) goto cancel;
        await LongTask4(item);
        if (token.IsCancellationRequested) goto cancel;
        await LongTask5(item);
        continue;
        cancel:
        {
            Log($"Cancelled during {item}");
            await SpinDownServiceAsync();
            return;
        }
    }
}

这是我看到的下一个最佳选择:

public Task Process(CancellationTokenSource token)
{
    await SpinUpServiceAsync();
    foreach (var item in LongList())
    {
        if (token.IsCancellationRequested)
        {
            Log($"Cancelled during {item}");
            await SpinDownServiceAsync();
            return;
        }
        await LongTask1(item);
        if (token.IsCancellationRequested)
        {
            Log($"Cancelled during {item}");
            await SpinDownServiceAsync();
            return;
        }
        await LongTask2(item);
        if (token.IsCancellationRequested)
        {
            Log($"Cancelled during {item}");
            await SpinDownServiceAsync();
            return;
        }
        await LongTask3(item);
        if (token.IsCancellationRequested)
        {
            Log($"Cancelled during {item}");
            await SpinDownServiceAsync();
            return;
        }
        await LongTask4(item);
        if (token.IsCancellationRequested)
        {
            Log($"Cancelled during {item}");
            await SpinDownServiceAsync();
            return;
        }
        await LongTask5(item);
    }
}

但是看看那些不必要的重复。

谢谢您的考虑。

在这种情况下,有比GOTO更好的方法吗?

我没有LongTask1-5的确切类型,但我会这样做。

public Task Process(CancellationTokenSource token)
{
    SOMETYPE[] tasks = new SOMETYPE[] {LongTask1, LongTask2, LongTask3, LongTask4, LongTask5};
    await SpinUpServiceAsync();
    foreach (var item in LongList())
    {
        foreach(var task in tasks){
            if (token.IsCancellationRequested) {
                Log($"Cancelled during {item}");
                await SpinDownServiceAsync();
                return;
            }
            await task(item);
        }

    }
} 

这种方法将任务放在一个数组中。如果需求发生变化,并且需要更多的任务,那么数组定义会变长,但其余代码保持不变。好的代码使用循环来完成重复的任务,而不是大量的复制和粘贴,比如在原来的帖子中。

好的,这是我的新版本(它可以编译)

准备代码(黑客)

class TypeOfItem { }
async Task LongTask1(TypeOfItem item) { }
async Task LongTask2(TypeOfItem item) { }
async Task LongTask3(TypeOfItem item) { }
async Task LongTask4(TypeOfItem item) { }
async Task LongTask5(TypeOfItem item) { }
async Task SpinUpServiceAsync() { }
async Task SpinDownServiceAsync() { }
IEnumerable<TypeOfItem> LongList() { yield break; }
void Log(string text) { }

和解决方案:

private async Task<bool> CancelAt(TypeOfItem item)
{
  Log($"Cancelled during {item}");
  await SpinDownServiceAsync();
  return false; // cancel
}
public async Task<bool> Process(CancellationTokenSource token)
{
  await SpinUpServiceAsync();
  foreach (var item in LongList())
  {
    if (token.IsCancellationRequested) return await CancelAt(item);
    await LongTask1(item);
    if (token.IsCancellationRequested) return await CancelAt(item);
    await LongTask2(item);
    if (token.IsCancellationRequested) return await CancelAt(item);
    await LongTask3(item);
    if (token.IsCancellationRequested) return await CancelAt(item);
    await LongTask4(item);
    if (token.IsCancellationRequested) return await CancelAt(item);
    await LongTask5(item);
  }
  return true; // all done
}

@codenoire:很好的解决方案,但需要一些修复…: -)

public async Task Process(CancellationTokenSource token)
{
  var tasks = new Func<TypeOfItem, Task>[] { LongTask1, LongTask2, LongTask3, LongTask4, LongTask5 };
  await SpinUpServiceAsync();
  foreach (var item in LongList())
  {
    foreach (var task in tasks)
    {
      if (token.IsCancellationRequested)
      {
        Log($"Cancelled during {item}");
        await SpinDownServiceAsync();
        return;
      }
      await task(item);
    }
  }
}

与@codenoir非常相似,但我更喜欢这样写:

private Action<TypeItem>[] _longTasks = new Action<TypeItem>[]
// Not sure about the type
// that could be also something like Func<TypeItem, Task>
    {
        LongTask1,
        LongTask2,
        LongTask3,
        LongTask4,
        LongTask5
    };
public async Task Process(CancellationTokenSource token)
{
    await SpinUpServiceAsync();
    foreach (var item in LongList())
        foreach (var longTask in _longTasks)
        {
            if (token.IsCancellationRequested)
                return Cancel(item);
            await longTask(item);
        }
}
private async Task Cancel(TypeItem item)
{
    Log($"Cancelled during {item}");
    await SpinDownServiceAsync();
}

似乎你在你的方法中也缺少async关键字。

简单的方法…

public async Task Process(CancellationTokenSource token)
{
    await SpinUpServiceAsync();
    bool cancel;
    YourObject item;
    foreach (var i in LongList())
    {
        item = i;
        cancel = true;
        if (token.IsCancellationRequested) break;
        await LongTask1(item);
        if (token.IsCancellationRequested) break;
        await LongTask2(item);
        if (token.IsCancellationRequested) break;
        await LongTask3(item);
        if (token.IsCancellationRequested) break;
        await LongTask4(item);
        if (token.IsCancellationRequested) break;
        await LongTask5(item);
        cancel = false;
    }
    if (cancel)
    {
        Log($"Cancelled during {item}");
        await SpinDownServiceAsync();
        return;
    }
}

…清洁…

public async Task Process(CancellationTokenSource token)
{
    var tasks = new Func<YourItem, Task>[] { LongTask1, LongTask2, LongTask3, LongTask4, LongTask5 };
    await SpinUpServiceAsync();
    foreach (var item in LongList())
    {
        foreach(var task in tasks)
        {
            if (token.IsCancellationRequested)
            {
                Log("Cancelled during {item}");
                await SpinDownServiceAsync();
                return;
            }
            await task(item);
        }
    }
}

您可以简单地在try/catch语句中包围if s块,并在等待取消时抛出异常。

伪代码:

function {
 try {
   for (loop conditions) {
     do_stuff_or_throw_exception1();
     do_stuff_or_throw_exception2();
     ...
     do_stuff_or_throw_exceptionN();
   }
   catch (exception) {
     log_stuff();
     shut_down()
   }
 }
}