3
\$\begingroup\$

This is a code automatically converted from VB 6.0 to C#.net What is a safe and correct way to get rid of that GOTO statement?

public bool IsDirty(bool checkChildrenInd)
{
    bool result = false;
    //Determine if this object is 'dirty', or has been updated since it was created.

    if (Parent.NewInd)
    {
        return true;
    }

    if (backup == null)
    {
        goto CheckChildren; //Check any child lists
    }

    if (!String.Equals(MissingDataValue, backup.MissingDataValue))
    {
        return true;
    }

    if (!String.Equals(PreviewDataValue, backup.PreviewDataValue))
    {
        return true;
    }

    if (FieldOption != backup.FieldOption)
    {
        return true;
    }

CheckChildren:
    if (!checkChildrenInd)
    {
        return result;
    }
    return result;
}
\$\endgroup\$
7
  • 7
    \$\begingroup\$ are you sure this is all? checkChildren will always return false. The whole thing could be rewritten as public bool IsDirty(bool checkChildrenInd){ return Parent.NewId || backup != null) \$\endgroup\$
    – Martijn
    Commented Sep 18, 2012 at 14:05
  • 1
    \$\begingroup\$ I agree with Martijn: it seems like "Check any child lists" is not actually checking any child lists whatsoever. \$\endgroup\$ Commented Sep 18, 2012 at 14:09
  • \$\begingroup\$ yes that's all. The PATTERN is always like the method above. Now for some other classes in the code I may have so much more things to check after the (if backup == null) and before GOTO ... and also so much more stuff to check inside the GOTO section as well...but the pattern is like this. \$\endgroup\$ Commented Sep 18, 2012 at 14:13
  • 1
    \$\begingroup\$ @Martijn : well how about those IFs for MissingDataValue in String.Equals? aren't we missing them with your code? how? \$\endgroup\$ Commented Sep 18, 2012 at 14:18
  • 2
    \$\begingroup\$ @BDotA If you wish to work on the pattern and not on this particular instance, I recommend leaving comments like // do something in the places where your code might (or not) do something. (Just make sure that the "do something" has no side-effects to the existing instructions... otherwise it's required, for the refactor.) \$\endgroup\$
    – ANeves
    Commented Sep 18, 2012 at 16:12

2 Answers 2

5
\$\begingroup\$

The minimal amount of changes to get rid of the goto is:

public bool IsDirty(bool checkChildrenInd) {
    bool result = false;
    //Determine if this object is 'dirty', or has been updated since it was created.
    if (Parent.NewInd) {
        return true;
    }
    if (backup != null) {
        if (!String.Equals(MissingDataValue, backup.MissingDataValue)) {
            return true;
        }
        if (!String.Equals(PreviewDataValue, backup.PreviewDataValue)) {
            return true;
        }
        if (FieldOption != backup.FieldOption) {
            return true;
        }
    }
    if (!checkChildrenInd) {
        return result;
    }
    return result;
}

Or if you want some refactoring:

public bool IsDirty(bool checkChildrenInd) {
    //Determine if this object is 'dirty', or has been updated since it was created.
    return Parent.NewInd
        || backup != null && CheckBackup()
        || checkChildrenInd && CheckChildren();
}

protected bool CheckBackup() {
    return !String.Equals(MissingDataValue, backup.MissingDataValue)
        || !String.Equals(PreviewDataValue, backup.PreviewDataValue)
        || FieldOption != backup.FieldOption;
}

protected bool CheckChildren() {
    return false; // or delegate, when there are any children
}
\$\endgroup\$
5
\$\begingroup\$

EDIT: FIXED

This specific instance can be rewritten as

public bool IsDirty(bool checkChildrenInd) {
  return ! (  Parent.NewId ||
              backup == null ||
              String.Equals(MissingDataValue, backup.MissingDataValue) ||
              String.Equals(PreviewDataValue, backup.PreviewDataValue) ||
              FieldOption == backup.FieldOption );
}

if this pattern is always the same, but there may be other conditions as you note in the comments, I note that CheckChildren

  • Takes a parameter which is also the function parameter
  • returns on all code paths
  • result is never assigned to before the goto statement

If this is always true, you can factor it out. replace the CheckChildren label with a function:

private bool CheckChildren(bool checkChildrenInd){
    bool result = false;
    if (!checkChildrenInd)
    {
        return result;
    }
    return result;
}

and the goto CheckChildren; with return CheckChildren(checkChildrenInd);

if result is possibly set to true before the goto statement, you will need to pass result as a variable to the funcion too (and include it in the signature).

\$\endgroup\$
5
  • \$\begingroup\$ Thanks. Just one last question: How about those IFs for MissingDataValue in String.Equals? aren't we missing them with your code? how? \$\endgroup\$ Commented Sep 18, 2012 at 14:22
  • 1
    \$\begingroup\$ The only path that returns false is if Parent.FirstId is false (otherwise it would have returned true) and backup == null. All other paths return true. \$\endgroup\$
    – Martijn
    Commented Sep 18, 2012 at 14:25
  • 1
    \$\begingroup\$ No, you're right. It falls through to the goto label, and if all checks return false, it returns result, which is initialised as false and never changed. Will update the answer accordingly. \$\endgroup\$
    – Martijn
    Commented Sep 18, 2012 at 14:31
  • 1
    \$\begingroup\$ @Martijn: sorry, but this is completely flawed -- first, you inverted the result for Parent.NewInd, you return false where the original returned true -- then if MissingDataValue is clean, but PreviewDataValue is dirty, you still return false instead of true, because the code then never checks PreviewDataValue -- last, you should call CheckChildren() in place of goto AND at the end... \$\endgroup\$
    – charlie
    Commented Sep 19, 2012 at 10:21
  • \$\begingroup\$ @Martijn: You tried to negate the condition to return ! IsClean();. But in that case the correct expression would look like: return ! ( ! Parent.NewId && ( backup == null || String.Equals(MissingDataValue, backup.MissingDataValue) && String.Equals(PreviewDataValue, backup.PreviewDataValue) && FieldOption == backup.FieldOption ) );. Be careful with the logic inversions. \$\endgroup\$
    – charlie
    Commented Sep 19, 2012 at 13:20

Not the answer you're looking for? Browse other questions tagged or ask your own question.