清晰或不清楚:C#多个三元运算符+抛出如果不匹配

你觉得下面的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#。 但是,你能够在第一眼或第二眼看到它吗? 还是我偏离了太远?

我已经在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 这种模式…我会使用开关/大小写。 这可以是非常紧凑,如果你想要:

 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); } 

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

 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)); } } 

我的经验法则:使用expression式的东西没有副作用。 使用语句来处理一个副作用和控制stream。

投掷实际上是一个副作用; 它不计算一个值,它改变了控制stream。 你正在计算价值,计算,计算,计算,然后繁荣,副作用。 我觉得这样的代码令人困惑和烦恼。 我说控制stream程应该在声明中,而不是看起来像计算的东西的副作用。

我投票不清晰。

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

可读性绝对是良好编码的一个关键因素,而且我认为您的示例对大多数开发人员来说不是立即可读的。

我喜欢有条件的运算符,并使用它很多。

这个例子有点令人困惑,因为从布局和用法来看,操作符的性质并不明确。

至less我喜欢通过使用这种格式来使select和select清楚:

 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语句,或者更好的命令模式等devise模式会更清晰。

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

如果/然后将是可取的。

将嵌套三元组转换为开关。 从来没有强迫一个控制结构变得糟糕或不可靠,特别是如果没有明显的好处,内置的结构将会完成什么。

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

所以清晰可读的不是非常复杂的(即可疑的)读者。 这让我觉得聪明是为了聪明。

有什么理由不在这里使用开关或其他构造?

为什么不使用可空typesbool?

 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; 

}

起初我很震惊,但实际上我想不出用C#编写这种更清晰的方法 – 我试图想到一些将Funcs映射到结果的东西,但它变得更加丑陋。

尽pipe通过实际的条件parsing是粗糙的,但至less可以轻松地理解这个意图,尽pipe我更喜欢使用一个开关块,并将其他所有事情作为特例处理。

太难阅读了,先照顾例外。

如果处理每个案例,那么你可以有更复杂的条件。

这是less数几次之一,这种方法中的许多单独的回报是可以接受的

 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); } 

对我来说看起来不错,但我会改变你的Throw方法:

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

这使您可以指定抛出的exception种类。

不幸的是,三元运算符(?:)不是C语言中常见的习惯用法,我遇到过许多C,C ++和C#开发人员,他们不得不暂停阅读,因为他们不熟悉或不用它。 这并不是一个糟糕的语言function或难以辨认,但是这些开发人员可能会称OP的例子难以辨认,因为它嵌套了一个他们不习惯的语言function。

我不觉得这个例子难以辨认 – 我曾多次看到嵌套的三元运算符。 但是,我确实认为使用开关是检查string的“参数”的最佳select。

更让人烦恼的是抛弃了“this”参数的扩展方法。 42.Throw(…)是什么意思? 如果我正在审查代码,我会把它称为糟糕的devise。

在C&C ++中,所描述的用法是习惯用法和操作符的原因。 三元条件与链接if-then-else的好处是它是一个已知types的expression式。 在C ++中,你实际上可以写

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

注意这个逗号运算符,它返回一个foo_t,它永远不会被抛出。

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