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

时间:2016-08-19 21:29:09

标签: c#

让我们以诚实的态度开始吧。我不是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);
    }
}

但看看那不必要的重复。

感谢您的考虑。

5 个答案:

答案 0 :(得分:14)

我没有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);
        }



    }
} 

此方法将任务放在数组中。如果需求发生变化,需要更多任务,那么数组定义会变长,但其余代码保持不变。好的代码使用循环来执行重复性任务,而不是像原始帖子中那样进行大量的复制和粘贴。

答案 1 :(得分:1)

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

准备代码(黑客入侵)

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);
    }
  }
}

答案 2 :(得分:0)

简单方法......

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);
        }
    }
}

答案 3 :(得分:0)

和@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关键字。

答案 4 :(得分:-2)

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

伪代码:

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()
   }
 }
}