具有多个返回点的重构方法

时间:2009-10-29 17:50:02

标签: c# refactoring

**编辑:下面有几个选项可行。请根据您对此事的看法进行投票/评论。

我正在使用以下基本结构清理和添加c#方法的功能:

    public void processStuff()
    {
        Status returnStatus = Status.Success;
        try
        {
            bool step1succeeded = performStep1();
            if (!step1succeeded)
                return Status.Error;

            bool step2suceeded = performStep2();
            if (!step2suceeded)
                return Status.Warning;

            //.. More steps, some of which could change returnStatus..//

            bool step3succeeded = performStep3();
            if (!step3succeeded)
                return Status.Error;
        }
        catch (Exception ex)
        {
            log(ex);
            returnStatus = Status.Error;
        }
        finally
        {
            //some necessary cleanup
        }

        return returnStatus;
    }

有许多步骤,在大多数情况下,步骤x必须成功才能进入步骤x + 1。现在,我需要添加一些总是在方法结束时运行的功能,但这取决于返回值。我正在寻找关于如何干净地重构这个以达到预期效果的建议。显而易见的选择是将依赖于返回值的功能放在调用代码中,但我无法修改调用者。

一个选项:

    public void processStuff()
    {
        Status returnStatus = Status.Success;
        try
        {
            bool step1succeeded = performStep1();
            if (!step1succeeded)
            {
                returnStatus =  Status.Error;
                throw new Exception("Error");
            }

            bool step2succeeded = performStep2();
            if (!step2succeeded)
            {
                returnStatus =  Status.Warning;
                throw new Exception("Warning");
            }

            //.. the rest of the steps ..//
        }
        catch (Exception ex)
        {
            log(ex);
        }
        finally
        {
            //some necessary cleanup
        }
        FinalProcessing(returnStatus);
        return returnStatus;
    }

这对我来说似乎有点难看。相反,我可以从performStepX()方法中抛出。但是,这会留下在processStuff()的catch块中适当设置returnStatus变量的问题。您可能已经注意到,处理步骤失败时返回的值取决于哪个步骤失败。

    public void processStuff()
    {
        Status returnStatus = Status.Success;
        try
        {
            bool step1succeeded = performStep1(); //throws on failure
            bool step2succeeded = performStep2(); //throws on failure

            //.. the rest of the steps ..//
        }
        catch (Exception ex)
        {
            log(ex);
            returnStatus = Status.Error;  //This is wrong if step 2 fails!
        }
        finally
        {
            //some necessary cleanup
        }
        FinalProcessing(returnStatus);
        return returnStatus;
    }

我很感激您的任何建议。

10 个答案:

答案 0 :(得分:9)

不要使用异常来控制程序的流程。就个人而言,我会保留代码。要在最后添加新功能,您可以这样做:

    public void processStuff()
    {
        Status returnStatus = Status.Success;
        try
        {
            if (!performStep1())
                returnStatus = Status.Error;
            else if (!performStep2())
                returnStatus = Status.Warning;

            //.. More steps, some of which could change returnStatus..//

            else if (!performStep3())
                returnStatus = Status.Error;
        }
        catch (Exception ex)
        {
            log(ex);
            returnStatus = Status.Error;
        }
        finally
        {
            //some necessary cleanup
        }

        // Do your FinalProcessing(returnStatus);

        return returnStatus;
    }

答案 1 :(得分:3)

您可以将步骤重构为接口,以便每个步骤(而不是方法)公开Run()方法和Status属性 - 您可以在循环中运行它们,直到遇到异常。这样,您就可以保留有关所执行步骤的完整信息,以及每个步骤的状态。

答案 2 :(得分:2)

您可以在finally部分执行处理。 finally很有趣,即使你已经在try块中返回,finally块仍会在函数实际返回之前执行。它会记住返回的值,所以你也可以在函数的最后放弃return

public void processStuff()
{
    Status returnStatus = Status.Success;
    try
    {
        if (!performStep1())
            return returnStatus = Status.Error;

        if (!performStep2())
            return returnStatus = Status.Warning;

        //.. the rest of the steps ..//
    }
    catch (Exception ex)
    {
        log(ex);
        return returnStatus = Status.Exception;
    }
    finally
    {
        //some necessary cleanup

        FinalProcessing(returnStatus);
    }
}

答案 3 :(得分:1)

获取元组课程。然后做:

var steps = new List<Tuple<Action, Status>>() {
  Tuple.Create(performStep1, Status.Error),
  Tuple.Create(performStep2, Status.Warning)
};
var status = Status.Success;
foreach (var item in steps) {
  try { item.Item1(); }
  catch (Exception ex) {
    log(ex);
    status = item.Item2;
    break;
  } 
}
// "Finally" code here

哦,是的,您可以使用元组的匿名类型:

var steps = new [] {                        
    { step = (Action)performStep1, status = Status.Error }, 
    { step = (Action)performStep2, status = Status.Error }, 
};                                          
var status = Status.Success          
foreach (var item in steps) {               
  try { item.step(); }                     
  catch (Exception ex) {                    
    log(ex);                                
    status = item.status;                    
    break;                                  
  }                                         
}                                           
// "Finally" code here                      

答案 4 :(得分:1)

扩展上面的接口方法:

public enum Status { OK, Error, Warning, Fatal }

public interface IProcessStage {
    String Error { get; }
    Status Status { get; }
    bool Run();
}

public class SuccessfulStage : IProcessStage {
    public String Error { get { return null; } }
    public Status Status { get { return Status.OK; } }
    public bool Run() { return performStep1(); }
}

public class WarningStage : IProcessStage {
    public String Error { get { return "Warning"; } }
    public Status Status { get { return Status.Warning; } }
    public bool Run() { return performStep2(); }
}

public class ErrorStage : IProcessStage {
    public String Error { get { return "Error"; } }
    public Status Status { get { return Status.Error; } }
    public bool Run() { return performStep3(); }
}

class Program {
    static Status RunAll(List<IProcessStage> stages) {
        Status stat = Status.OK;
        foreach (IProcessStage stage in stages) {
            if (stage.Run() == false) {
                stat = stage.Status;
                if (stat.Equals(Status.Error)) {
                    break;
                }
            }
        }

        // perform final processing
        return stat;
    }

    static void Main(string[] args) {
        List<IProcessStage> stages = new List<IProcessStage> {
            new SuccessfulStage(),
            new WarningStage(),
            new ErrorStage()
        };

        Status stat = Status.OK;
        try {
            stat = RunAll(stages);
        } catch (Exception e) {
            // log exception
            stat = Status.Fatal;
        } finally {
            // cleanup
        }
    }
}

答案 5 :(得分:0)

你可以让performStep1performStep2抛出不同的例外吗?

答案 6 :(得分:0)

您可以撤消状态设置。在调用异常抛出方法之前设置错误状态。最后,如果没有例外,请将其设置为成功。

Status status = Status.Error;
try {
  var res1 = performStep1(); 
  status = Status.Warning;
  performStep2(res1);
  status = Status.Whatever
  performStep3();
  status = Status.Success; // no exceptions thrown
} catch (Exception ex) {
  log(ex);
} finally {
 // ...
}

答案 7 :(得分:0)

我不了解你的逻辑要求是什么,我首先创建一个抽象类来充当基础对象来执行特定步骤并返回执行状态。它应具有可重写的方法来实现任务执行,成功操作和失败操作。还处理逻辑将逻辑放在此类中以处理任务的成功或失败:

abstract class TaskBase
{
    public Status ExecuteTask()
    {
        if(ExecuteTaskInternal())
            return HandleSuccess();
        else
            return HandleFailure();
    }

    // overide this to implement the task execution logic
    private virtual bool ExecuteTaskInternal()
    {
        return true;
    }

    // overide this to implement logic for successful completion
    // and return the appropriate success code
    private virtual Status HandleSuccess()
    {
        return Status.Success;
    }

    // overide this to implement the task execution logic
    // and return the appropriate failure code
    private virtual Status HandleFailure()
    {
        return Status.Error;
    }
}

在创建Task类以执行步骤之后,按执行顺序将它们添加到SortedList,然后在任务完成时遍历它们检查staus:

public void processStuff()
{
    Status returnStatus 
    SortedList<int, TaskBase> list = new SortedList<int, TaskBase>();
    // code or method call to load task list, sorted in order of execution.
    try
    {
        foreach(KeyValuePair<int, TaskBase> task in list)
        {
            Status returnStatus task.Value.ExecuteTask();
            if(returnStatus != Status.Success)
            {
                break;
            }
        }
    }
    catch (Exception ex)
    {
        log(ex);
        returnStatus = Status.Error;
    }
    finally
    {
        //some necessary cleanup
    }

    return returnStatus;
}

我离开了错误处理,因为我不确定你在任务执行时的陷阱错误,或者只是在寻找你在特定步骤上抛出的异常是否失败。

答案 8 :(得分:0)

我建议将这些步骤重构为单独的课程,毕竟,你的课程应该只有一个单一的责任。我认为听起来应该控制步骤的运行。

答案 9 :(得分:-1)

嵌套if怎么样?

可行,但无法正常工作

它会删除每个返回只留下一个

if(performStep1())
{
  if(performStep2())
  {
      //..........
  }
  else 
    returnStatus = Status.Warning;
}
else
  returnStatus = Status.Error;