How to prevent recursion

岁酱吖の 提交于 2019-12-25 06:54:32

问题


Given the class named OrderInfo, what's the best way to ensure other developers (including myself) do not accidentally make the recursive error shown here:

public class OrderInfo : ICollection<UnitModule> {

  // Other code for class...

  public bool Changed { get; private set; }

  public void Save() {
    Save(this); // I want the static method to handle saving the data
  }

  public static void Save(OrderInfo item) {
    for (int i = 0; i < item.Count; i++) {
      if (item[i].Changed) {
        item[i].Save();
      }
    }
    if (item.Changed) {
      item.Save(); // this would be bad!
      // Instead, all of the other developers should have to call the
      // Database Save method.
      // Is there a way to ensure this happens or do I have to rely on
      // everyone remembering this?
    }
  }

}

EDIT: Using the marked answer, I can write my class as follows (why does not matter - this simply prevents the recursion):

public class OrderInfo : ICollection<UnitModule> {
  // Other code for class...
  bool saving; // <= new variable
  public bool Changed { get; private set; }

  public void Save() {
    if (!saving) {
      Save(this);
    } else {
      throw new Exception("This item is already being saved.");
    }
  }

  public static void Save(OrderInfo item) {
    item.saving = true;
    try {
      for (int i = 0; i < item.Count; i++) {
        if (item[i].Changed) {
          item[i].Save();
        }
      }
      if (item.Changed) {
        // item.Save(); <= NOTE: this would throw an exception
        DataAccess.Save(item);
        item.Changed = false;
      }
    } finally {
      item.saving = false;
    }
  }
}

回答1:


This might work:

public static void Save(OrderInfo item) {
  for (int i = 0; i < item.Count; i++) {
    if (item[i].Changed) {
      item[i].Save();
    }
  }
  if (item.Changed) {
    item.Changed = false; // prevents recursion!
    item.Save();
    if error saving then item.Changed = true; // reset if there's an error
  }
}



回答2:


Typically, the crash when stack limits are exceeded serves as a warning that something's wrong. As long as code gets even cursory testing before going into production, somebody will catch this bug.




回答3:


I don't think your problem's unwanted recursion. I think your problem is that you've implemented a static method that takes an instance of the class as its only argument.

Why have you even created a static method in the first place? What's the use case for any method with the signature public void Foo.Bar(Foo f)? Why would you ever want to use Foo.Bar(f) instead of f.Bar()? There may be good answers to this question, I don't know. But that's what I'd figure out first.




回答4:


I would use one of the following methods in order of preference:

  • refactor the code to prevent developer error
  • apply a static analysis tool such as FxCop with a fairly-specific rule that checks this case or similar cases
  • add an if-check of some kind to prevent the call
  • if runtime performance was not an issue, look at the stack frame to find out the state of the call, and at least throw an exception. This is very brittle and ugly though.



回答5:


This is looking like a code smell (if something can "look" like a "smell"). I would look at refactoring the class. Why does OrderInfo have a public static method that looks like it can be used for saving other OrderInfo instances?




回答6:


you could do this:

private bool saving;
public void Save() 
{
    if(this.saving)
    {
        throw new InvalidOperationException("accidental recursion");
    }
    try
    {
        this.saving = true;
        OrderInfo.Save(this); // I want the static method to handle saving the data
    }
    finally
    {
        this.saving = false;
    }
}

but as others have said you should probably refactor your code to be less error prone.



来源:https://stackoverflow.com/questions/6051501/how-to-prevent-recursion

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!