重构ifs在C#中嵌套语句

时间:2011-03-01 12:25:40

标签: c# if-statement

如果可以减少嵌套“if”语句,我想重构我的代码,使其更加优雅和清晰。

我的代码“解析”MsBuil进程的任何字符串(结果),以检查进程是否正确构建。

public static bool CheckMsBuildResult(string resultadoEjecucionScriptMsBuild)
        {
        // Build started 01/09/2010 8:54:07.
        string s1 = @"Build Started \d{1,2}/\d\d/\d{4} \d{1,2}:\d\d:\d\d";

        //Build started 9/1/2010 10:53:35 AM.
        //Build started 9/1/2010 8:42:16 AM.
        string s1Mod = @"Build Started \d{1,2}/\d{1,2}/\d{4} \d{1,2}:\d\d:\d\d";

        s1 = s1Mod;

        string s11 = @"n Generar iniciada a las \d{1,2}/\d\d/\d{4} \d{1,2}:\d\d:\d\d";

        // Compilaci�n iniciada a las 28/02/2011 14:56:55.
        string s12 = @"Compilaci.n iniciada a las \d{1,2}/\d\d/\d{4} \d{1,2}:\d\d:\d\d";


        string s2 = "Build succeeded.";
        string s21 = @"Generaci.n satisfactoria\.";
        string s3 = @"0 Error\(s\)";
        string s31 = "0 Errores";

        Regex rg = new Regex(s1, RegexOptions.Multiline | RegexOptions.IgnoreCase);
        Match mt = rg.Match(resultadoEjecucionScriptMsBuild);
        if (!mt.Success)
        {
            rg = new Regex(s11, RegexOptions.Multiline | RegexOptions.IgnoreCase);
            mt = rg.Match(resultadoEjecucionScriptMsBuild);
            if (!mt.Success)
            {
                rg = new Regex(s12, RegexOptions.Multiline | RegexOptions.IgnoreCase);
                mt = rg.Match(resultadoEjecucionScriptMsBuild);

                if (!mt.Success) return false;
            }
        }

        int i = mt.Index + mt.Length;

        rg = new Regex(s2, RegexOptions.Multiline | RegexOptions.IgnoreCase);
        mt = rg.Match(resultadoEjecucionScriptMsBuild, i);
        if (!mt.Success)
        {
            rg = new Regex(s21, RegexOptions.Multiline | RegexOptions.IgnoreCase);
            mt = rg.Match(resultadoEjecucionScriptMsBuild);
            if (!mt.Success)
            {
                return false;
            }
        }

        i = mt.Index + mt.Length;

        rg = new Regex(s3, RegexOptions.Multiline | RegexOptions.IgnoreCase);
        mt = rg.Match(resultadoEjecucionScriptMsBuild, i);
        if (!mt.Success)
        {
            rg = new Regex(s31, RegexOptions.Multiline | RegexOptions.IgnoreCase);
            mt = rg.Match(resultadoEjecucionScriptMsBuild);
            if (!mt.Success)
            {
                return false;
            }
        }

        return true;
        }

5 个答案:

答案 0 :(得分:5)

看起来你可以通过使用一个更强大的正则表达式而不是s1s1mods12等来将其减少到一行或两行代码。但是,既然你已经修改了你的正则表达式,我无法确切地告诉你它的样子。

我也批评这段代码

  • Undescriptive variable names。
  • 不必要的重复使用变量
  • 复制
  • 方法名称不好(确切地说是什么?)
  • 如果经常调用此方法,由于缺少重复使用已编译的正则表达式,性能可能会很差。

答案 1 :(得分:1)

我认为你可以在这里减少很多重复的代码。

编辑(删除了一些重复的代码并发布了linq方法):

public static Match MatchOptions(string resultadoEjecucionScriptMsBuild, int index, params string[] strings)
{
    return strings.Select(s => new Regex(s, RegexOptions.Multiline | RegexOptions.IgnoreCase))
        .Select(rg => rg.Match(resultadoEjecucionScriptMsBuild, index)).FirstOrDefault(mt => mt != null);
}

或者如果你不喜欢linq:

public static Match MatchOptions(string resultadoEjecucionScriptMsBuild, int index, params string[] strings)
{
    foreach (string s in strings)
    {
        Regex rg = new Regex(s, RegexOptions.Multiline | RegexOptions.IgnoreCase);
        Match mt = rg.Match(resultadoEjecucionScriptMsBuild, index);

        if (mt != null)
            return mt;
    }

    return null;
}

public static bool CheckMsBuildResult(string resultadoEjecucionScriptMsBuild)
{
    string s1 = @" ..... ";
    string s1Mod = @" ..... ";
    string s11 = @" ..... ";

    // Compilaci�n iniciada a las 28/02/2011 14:56:55.
    string s12 = @" ..... ";

    string s2 = @" ..... ";
    string s21 = @" ..... ";
    string s3 = @" ..... ";
    string s31 = @" ..... ";

    var stringsArray = new[]
                        {
                            new[] {s1, s11, s12},
                            new[] {s2, s21},
                            new[] {s3, s31}
                        };

    Match mt = null;
    int i = 0;
    foreach (string[] strings in stringsArray)
    {
        mt = MatchOptions(resultadoEjecucionScriptMsBuild, i, strings);
        if (mt == null)
            return false;

        i = mt.Index + mt.Length;
    }

    return mt != null;
}

答案 2 :(得分:1)

以下是两个想法:

  • 您似乎正在尝试使用嵌套ifdecision tree进行编码。一个更好的选择可能是定义一个对树进行编码的数据结构,然后以递归方式遍历树以测试匹配。

  • 我认为没有任何简单的方法可以重构代码以避免嵌套。如果你足够勇敢,可以使用LINQ语法。 (我的free chapter中有一个functional programming book解释 monads 如何工作以及如何使用LINQ编写这样的东西。

使用LINQ语法,您可以重写:

Regex rg = new Regex(s1, RegexOptions.Multiline | RegexOptions.IgnoreCase);
Match mt = rg.Match(resultadoEjecucionScriptMsBuild);
if (!mt.Success)
{
    rg = new Regex(s11, RegexOptions.Multiline | RegexOptions.IgnoreCase);
    mt = rg.Match(resultadoEjecucionScriptMsBuild);
    if (!mt.Success)
    {
        rg = new Regex(s12, RegexOptions.Multiline | RegexOptions.IgnoreCase);
        mt = rg.Match(resultadoEjecucionScriptMsBuild);
        if (!mt.Success) return false;
    }
}

......变成这样的东西(注意你需要定义相当多的帮助器而我没有显示完整的代码 - 所以你无法比较长度 - 但你可以看到它避免了嵌套):

var matches = 
    from rg1 in MatchRegex(s1, RegexOptions.Multiline | RegexOptions.IgnoreCase,
                          resultadoEjecucionScriptMsBuild)
    from rg2 in MatchRegex(s11, RegexOptions.Multiline | RegexOptions.IgnoreCase,
                          resultadoEjecucionScriptMsBuild)
    from rg3 in MatchRegex(s11, RegexOptions.Multiline | RegexOptions.IgnoreCase,
                          resultadoEjecucionScriptMsBuild)
    select true;

if (matches) return false;

如果您需要访问某些早期Match对象以确定最终结果(例如,最后使用rg1),这可能很有用。否则,您可能只需编写一个运行给定正则表达式的方法,并使用if将它们组合在一个大的&&语句中。

答案 3 :(得分:1)

第一个重构变量名。然后尝试使用布尔值展平代码,如下例所示:

    Regex rg = new Regex(s1, RegexOptions.Multiline | RegexOptions.IgnoreCase);
    bool match_s1 = rg.Match(resultadoEjecucionScriptMsBuild).Success;        

    rg = new Regex(s11, RegexOptions.Multiline | RegexOptions.IgnoreCase);
    bool match_s11 = rg.Match(resultadoEjecucionScriptMsBuild).Success;         

    rg = new Regex(s12, RegexOptions.Multiline | RegexOptions.IgnoreCase);        
    bool match_s12 = rg.Match(resultadoEjecucionScriptMsBuild).Success;                 

    if (!match_s1 && !match_s11) 
        return match_s12; 

答案 4 :(得分:0)

如果输入包含s1|s11|s12之一,后跟s2|s21之一,最后是s3|s31之一,则您似乎要测试。

你总是可以制作一个更复杂的正则表达式来反映这一点:

var complex = "((" + s1 + ")|(" + s11 + ")|(" + s12 + "))|((" + s2 + ")|(" + s21 + ...

并在一场比赛中使用它。

如果你只是想摆脱嵌套的ifs(即如果你想要一个维持当前控制流的重构),我会将正则表达式分组到像

这样的列表中
var reList = new List<List<string>>() {
  new List<string>(){s1, s11, s12},
  new List<string>(){s2, s21},
  new List<string>(){s3, s31}
};

(使用C#3.0集合初始值设定项)

然后你可以迭代两个嵌套循环中的所有表达式,比如

var i = 0;
foreach (var outer in reList) {
  Match mt;
  foreach (var inner in outer) {
    rg = new Regex(inner, RegexOptions.Multiline | RegexOptions.IgnoreCase);
    mt = rg.Match(resultadoEjecucionScriptMsBuild, i);
    if (mt.Success)
      break;
  }
  if (!mt.Success)
    return false;
  i = mt.Index + mt.Length;
}
return true;

如果输入字符串是s1,s11,s12之一,s2, s21之一,最后是s3, s31之一的串联,则此函数返回true。