代码之家  ›  专栏  ›  技术社区  ›  user76035

是否清晰:C多个三元运算符+如果不匹配则抛出[关闭]

  •  34
  • user76035  · 技术社区  · 14 年前

    您是否发现以下C代码清晰可见?

    private bool CanExecuteAdd(string parameter) {
        return
            this.Script == null ? false
            : parameter == "Step" ? true
            : parameter == "Element" ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
            : parameter == "Choice" ? this.SelectedElement != null
            : parameter == "Jump" ? this.SelectedStep != null
            : parameter == "Conditional jump" ? false
            : false.Throw("Unknown Add parameter {0} in XAML.".F(parameter));
    }
    

    其中throw定义为:

    public static T Throw<T>(this T ignored, string message) {
        throw new Exception(message);
    }
    

    我知道这不是惯用的C。然而,你能在第一眼或第二眼就能理解它吗?还是我迷路太远了?

    15 回复  |  直到 12 年前
        1
  •  27
  •   Jon Skeet    14 年前

    我在爪哇使用了这类代码。我不喜欢 false.Throw 位,但在我看来,有多个这样的条件(特别是这样格式化的)是可以的。

    你第一次看到它的时候有点奇怪,但在那之后,它只是一个方便的模式。

    使用的一种选择 错误。投掷 这里是这样的:

    bool? ret = this.Script == null ? false
        : parameter == "Step" ? true
        : parameter == "Element" ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
        : parameter == "Choice" ? this.SelectedElement != null
        : parameter == "Jump" ? this.SelectedStep != null
        : parameter == "Conditional jump" ? false
        : null;
    
    if (ret == null)
    {
        throw new ArgumentException(
           string.Format("Unknown Add parameter {0} in XAML.", parameter);
    }
    return ret.Value;
    

    编辑:实际上,在这种情况下,我也不会使用if/else 这种模式…我会用开关/箱子。如果您需要,这可以非常紧凑:

    if (this.Script == null)
    {
        return false;
    }
    switch (parameter)
    {
        case "Step": return true;
        case "Element": return this.ElementSelectedInLibrary != null && this.SelectedStep != null;
        case "Choice": return this.SelectedElement != null;
        case "Jump": return this.SelectedStep != null;
        default: throw new ArgumentException(
            string.Format("Unknown Add parameter {0} in XAML.", parameter);
    }
    
        2
  •  29
  •   Fede    14 年前

    为什么不使用开关?我认为这是一种更具可读性的方式。

    private bool CanExecuteAdd(string parameter) {
        if (Script == null)
            return false;
    
        switch (parameter) {
            case "Step":
                return true;
            case "Element":
                return ElementSelectedInLibrary != null && SelectedStep != null;
            case "Choice":
                return SelectedElement != null;
            case "Jump":
                return SelectedStep != null;
            case "Conditional jump":
                return false;
            default:
                throw new Exception(string.Format("Unknown Add parameter {0} in XAML.", parameter));
        }
    }
    
        3
  •  10
  •   Eric Lippert    14 年前

    我的经验法则:对没有副作用的事物使用表达式。对具有单侧效应的事物和控制流使用语句。

    投掷实际上是一种副作用;它不计算值,而是改变控制流。你在计算一个值,计算,计算,计算,然后繁荣,副作用。我发现这样的代码令人困惑和烦恼。我说控制流应该在语句中,而不是看起来像计算的副作用。

        4
  •  8
  •   KP.    14 年前

    我投票赞成看不清。

    虽然语法是正确的,但有点复杂,而且我敢说,由于它不是“传统的”,许多开发人员将不得不浪费时间来确保他们理解他们正在阅读的内容。不是理想的情况。

    可读性绝对是良好编码的一个关键要素,我想说,对于大多数开发人员来说,您的示例不能立即读取。

        5
  •  6
  •   Community TheSoundDefense    7 年前

    我喜欢条件运算符,并且经常使用它。

    这个例子有点令人困惑,因为从布局和用法上看,运算符的性质并不清楚。

    至少我喜欢使用这种格式来明确选择和备选方案:

    choice
      ? true-case
      : false-case
    

    但如果我们将其应用到代码中,就会发现使用这种构造时缺乏清晰性:

    return
        this.Script == null 
                    ? false 
                    : parameter == "Step" 
                        ? true
                        : parameter == "Element" 
                            ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
                            : parameter == "Choice" 
                                ? this.SelectedElement != null
                                : parameter == "Jump" 
                                    ? this.SelectedStep != null
                                    : parameter == "Conditional jump" 
                                            ? false
                                            : false.Throw("Unknown Add parameter {0} in XAML.".F(parameter));
    

    在我看来,这就像我们试图使用条件运算符(如switch语句),其中switch语句或更好的设计模式(如 Command Pattern 会更清楚。

        6
  •  5
  •   David Morton    14 年前

    我真的不喜欢这个代码。我花了超过15秒才明白,所以我放弃了。

    如果/那么更好。

        7
  •  4
  •   Alison R.    14 年前

    将嵌套的三元转换为开关。不要强迫一个控制结构做得太差或难以辨认的内置结构会做得很好,特别是如果没有明显的好处。

        8
  •  3
  •   Ori Pessach    14 年前

    非习语意味着你强迫读者花时间思考他们所读的是否意味着他们所想的。

    因此,清晰易读并不能很好地吸引那些老练的(即可疑的)读者。这让我觉得是聪明是为了聪明。

    这里是否有理由不使用switch或else-if构造?

        9
  •  2
  •   sylvanaar    14 年前

    为什么不使用可以为空的类型bool?

    private bool? CanExecuteAdd(string parameter) {
    return
        this.Script == null ? false
        : parameter == "Step" ? true
        : parameter == "Element" ? this.ElementSelectedInLibrary != null && this.SelectedStep != null
        : parameter == "Choice" ? this.SelectedElement != null
        : parameter == "Jump" ? this.SelectedStep != null
        : parameter == "Conditional jump" ? false
        : null;
    

    }

        10
  •  2
  •   Ana Betts    14 年前

    起初我很害怕,但实际上我想不出一种用C语言写得更清楚的方法——我试着去想一个你把一系列有趣的东西映射到结果上的方法,但是它变得更丑了。

    尽管通过实际条件进行解析很困难,但至少可以轻松地摸索出意图,尽管我更喜欢使用开关块,并将其他一切作为特殊情况处理。

        11
  •  1
  •   CaffGeek    14 年前

    读起来太难了,先处理异常。

    在它自己的if中处理每个案例,那么您就可以有更复杂的条件。

    这是少数几次中的一次,一个方法中有这么多单独的返回是可以接受的

    private bool CanExecuteAdd(string parameter) 
    {       
        if (this.Script == null)
            return false;
    
        if (parameter.NotIn([] {"Step", "Element", "Choice", "Jump", "Conditional jump"})
            throw new Exception("Unknown Add parameter {0} in XAML.".F(parameter));
    
        if (parameter == "Step") 
            return true;
    
        if (parameter == "Element")
            this.ElementSelectedInLibrary != null && this.SelectedStep != null;
    
            // etc, etc
    }
    

    噢,.notin是一种扩展方法,与之相反,我想(不能说这和需要的完全一样)

    public static bool In<T>(this T obj, IEnumerable<T> arr)
    {
        return arr.Contains(obj);
    }
    
        12
  •  1
  •   Robert Davis    14 年前

    我觉得不错,但我会把你的投掷方法改为:

    static TObject Throw<TObject, TException>(this TObject ignored, TException exception)
    {
       throw exception;
    }
    

    这允许您指定引发异常的类型。

        13
  •  1
  •   Curt Nichols    14 年前

    不幸的是,三元运算符(?不是C语言中常见的成语——我遇到过很多C、C++和C开发人员,因为他们不熟悉它或者不使用它,所以不得不暂停阅读。这并不意味着它是一个糟糕的语言特性或难以辨认的,但是这些开发人员可能会将op的示例称为难以辨认的,因为它嵌套了一个他们不喜欢的语言特性。

    我不认为这个例子难以辨认——我见过很多次嵌套的三元运算符。但是,我确实认为使用开关是检查字符串的“参数”的首选选择。

    更令我恼火的是,抛出的扩展方法忽略了“this”参数。42.throw(…)是什么意思?如果我在检查代码,我会称它为糟糕的设计。

        14
  •  1
  •   Derrick    13 年前

    在C++C++中,所描述的用法是习语和算子的原因。三元条件与链接if-then-else的好处是它是一个具有已知类型的表达式。在C++中,你可以实际编写

    foo_t foo = bar_p ? bar
              : qux_p ? qux
              : woz_p ? woz
              : (throw SomeExpression(), foo_t())
              ;
    

    注意逗号操作符,它返回一个由于抛出而永远不会被构造的foo-t。

        15
  •  0
  •   ChrisLively    14 年前

    实际上,到目前为止,我从未见过三元运算符被推出。不过,我知道你要去哪里。另外,我同意乔恩的观点,我不喜欢这个错误。扔下一部分。