33

您觉得以下 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#。但是,您第一眼或第二眼就能理解它吗?还是我走得太远了?

4

15 回答 15

31

为什么不用开关?我认为它更具可读性。

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));
    }
}
于 2010-02-23T17:57:13.783 回答
28

我在 Java 中使用了相当多的这种代码。我不喜欢这个false.Throw位,但在我看来,有多个这样的条件(特别是这样格式化)很好。

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

在这里使用的一种替代方法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这种模式......我会使用 switch/case。如果您愿意,这可以非常紧凑:

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);
}
于 2010-02-23T17:43:05.677 回答
11

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

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

于 2010-02-23T21:10:07.647 回答
8

我投票给不可读的。

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

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

于 2010-02-23T17:47:35.270 回答
6

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

这个例子有点混乱,因为从布局和用法上算子的性质并不清楚。

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

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 语句,或者更好的是像命令模式这样的设计模式会更清晰。

于 2010-02-23T17:56:53.333 回答
5

我真的不喜欢这段代码。我花了大约 15 秒多才明白,所以我放弃了。

如果/那么会更好。

于 2010-02-23T17:46:31.217 回答
3

将嵌套的三元转换为开关。永远不要强迫一个控制结构做得很差或难以辨认,而内置结构会做得很好,尤其是在没有明显好处的情况下。

于 2010-02-23T17:54:10.437 回答
2

非惯用语意味着你迫使读者花时间思考他们所读的内容是否意味着他们认为的意思。

因此,清晰易读并不会购买复杂(即可疑)的阅读器。这让我觉得是为了聪明而聪明。

有什么理由不在这里使用 switch 或 else if 构造吗?

于 2010-02-23T17:44:28.383 回答
2

为什么不使用可空类型 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;

}

于 2010-02-23T17:48:08.663 回答
2

起初我很害怕,但实际上我想不出用 C# 写得更清楚的方法——我试图想出一些东西,你有一个函数数组映射到结果,但它变得更加丑陋。

尽管通过实际条件句进行解析很粗糙,但至少很容易理解意图,尽管我更喜欢使用 switch 块并将其他所有内容作为特殊情况处理。

于 2010-02-23T17:49:24.553 回答
1

太难读了,先处理异常。

在它自己的情况下处理每个案例,那么你可以有更复杂的条件。

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

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);
}
于 2010-02-23T18:26:33.840 回答
1

对我来说看起来不错,但我会将您的 Throw 方法更改为:

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

这允许您指定抛出的异常类型。

于 2010-02-23T19:03:37.863 回答
1

不幸的是,三元运算符(?用它。这不会使它成为一个糟糕的语言功能或难以辨认,但是那些相同的开发人员可能会称 OP 的示例难以辨认,因为它嵌套了他们不喜欢的语言功能。

我没有发现这个例子难以辨认——我已经多次看到嵌套的三元运算符。但是,我确实认为使用开关检查字符串的“参数”是一个更可取的选择。

更让我恼火的是忽略“this”参数的 Throw 扩展方法。42.Throw(...) 是什么意思?如果我正在审查代码,我会将其称为糟糕的设计。

于 2010-02-23T19:26:55.063 回答
1

在 C 和 C++ 中,所描述的使用是惯用的,也是运算符的原因。三元条件与链式 if-then-else 的好处是它是一个具有已知类型的表达式。在 C++ 中,您实际上可以编写

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

请注意逗号运算符,它返回一个 foo_t,由于 throw 将永远不会被构造。

于 2011-05-07T16:34:45.640 回答
0

到目前为止,我实际上从未见过三元运算符推出。不过,我明白你要去哪里。另外,我同意乔恩的观点,我不喜欢 false.Throw 部分。

于 2010-02-23T17:46:50.053 回答