如何将此Javascript代码段转换为C#

Ang*_*ker -4 javascript c#

Javascript:

function bounds(a, b, c) {
  null != b && (a = Math.max(a, b));
  null != c && (a = Math.min(a, c));
  return a
}
Run Code Online (Sandbox Code Playgroud)

老实说,我什至不知道这里发生了什么。如果b不为null,则a设置set Math.max(a, b)。如果c不为null,a则从设置Math.min(a, c)

如何将其转换为C#?

PS:这是我的翻译。它是否正确?

private double bounds(double a, double? b, double? c) {
    if (b != null) a = Math.Max(a, b.Value);
    if (c != null) a = Math.Min(a, c.Value);

    return a;
}
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 10

老实说,我什至不知道这里发生了什么。

我也不。该代码出现执行合同“,如果c是的最小元素,返回它,否则,返回的中间元素abc,再加上一些空性未知目的的检查。

这是一个离奇的合同,使我认为这可能是未经测试的“返回中间元素”的实现,其中存在错误。如果c实际上从来不是最小的元素,那么也许永远不会发现该错误。

如何将其转换为C#?

了解上下文中代码的含义,然后在惯用的C#中实现这些语义。重要的是理解。一旦理解了代码,编写正确,可测试的实现就会很简单。

这是我的翻译。它是否正确?

private double bounds(double a, double? b, double? c) {
  if (b != null) a = Math.Max(a, b.Value);
  if (c != null) a = Math.Min(a, c.Value);
  return a;
}
Run Code Online (Sandbox Code Playgroud)

正确的是,它可以准确地实现原始的,可能是错误的代码的语义。但这并没有更好。让我们改善它。突出三点:

  • 该方法不消耗this,所以应该消耗static
  • 该方法是C#方法,应遵循C#的命名约定。
  • 覆盖其形式的方法在设计时很难读取,在运行时很难调试。因此,任何使变量发生变化的方法都比不使变量发生变化的方法更难理解。
  • 命名变量abc,有含义给他们难以阅读,因为它们掩盖那些含义。

让我们修复所有这些。

private static double Bounds(double a, double? b, double? c) 
{
  double larger = (b == null) ? a : Math.Max(a, b.Value);
  double smaller = (c == null) ? larger : Math.Min(larger, c.Value);
  return smaller;
}
Run Code Online (Sandbox Code Playgroud)

我们仍然不具备良好的名称abc,但至少我们知道,largersmaller反映它们的含义。

我们完了吗?总是问自己,我该如何简化

您可以通过将支票移到帮助程序中来简化操作。

private static double Max(double x, double? y) =>
  (y == null) ? x : Math.Max(x, y.Value)

private static double Min(double x, double? y) =>
  (y == null) ? x : Math.Min(x, y.Value)
Run Code Online (Sandbox Code Playgroud)

现在我们的方法变为:

private static double Bounds(double a, double? b, double? c) 
{
  double larger = Max(a, b);
  double smaller = Min(larger, c);
  return smaller;
}
Run Code Online (Sandbox Code Playgroud)

现在我们意识到我们可以将其重写为

private static double Bounds(double a, double? b, double? c) => 
  Min(Max(a, b), c);
Run Code Online (Sandbox Code Playgroud)

当然,目前还不清楚这件事的目的是什么,但我希望你同意我的执行行动尚不明确。在我的实现中,语义显然是“如果c至少是,则返回它,否则返回中间元素”。将我的版本的可理解性与原始的混乱情况进行比较,看看哪一个更容易理解。

这是什么教训?

  • 将代码组织为单份分配形式
  • 将复杂的逻辑移到辅助方法中以将其抽象化
  • 重写代码以消除不必要的解释变量。
  • 该代码变得干净并且易于理解。

进一步的沉思;假设我的猜想是正确的,并且打算写:

private static double Middle(double test, double? bottom, double? top)
Run Code Online (Sandbox Code Playgroud)

该方法的语义是:

  • bottom并且top都可以为null。
  • 如果两者都不为空,则bottom必须较小。
  • 如果bottomtop都不都是null,则返回中间的值。
  • 如果bottomtop均为null,则test中间的值为。
  • 如果bottom为null但top不为null,则testand中的较小者top为中间的值。在这里,我们将“ null”(像“负无穷大”)当作底部。
  • 同样,如果top为null但bottom不为null ,则bottomand中的较大者test为中间的那个。在这里,空顶被视为“正无穷大”。

这实际上是一个非常明智的合同,但是它要求我们知道它bottom总是比top它们都不均为null时小。在C#中Debug.Assert,良好的实现可能是事实(如果方法为private),或者throw前提条件被违反(如果方法为public)。

如果是这种情况,那么更好的选择是这样做:

private static double Middle(double test, double? bottom, double? top) =>
  Middle(
    test, 
    bottom ?? Double.NegativeInfinity, 
    top ?? Double.PositiveInfinity);

private static double Middle(double test, double bottom, double top) =>
  Math.Min(Math.Max(test, bottom), top);
Run Code Online (Sandbox Code Playgroud)

(如果您不熟悉??,则将其读为“ coalesce”,x??y意思是“如果x不为null,则使用x.Value,否则使用y”。这是一个非常有用的运算符。)

再次,看一下代码的可读性,看它看起来多么漂亮和容易。很明显,“ null表示底部为负无穷大,顶部为正无穷大”,因为这就是代码所说的!

或者,编写不需要对三个输入进行任何排序的Middle的正确实现:

// True if test is between b1 and b2, false otherwise
private static bool Between(double test, double b1, double b2) =>
  ((b1 <= test) & (test <= b2)) | ((b2 <= test) & (test <= b1));

// Returns the middle value of a, b, and c.
private static double Middle(double a, double b, double c)
{
  if (Between(a, b, c) return a;
  if (Between(b, a, c) return b;
  return c;
}
Run Code Online (Sandbox Code Playgroud)

或者,如果您喜欢

private static double Middle(double a, double b, double c) =>
  Between(a, b, c) ? a : Between(b, a, c) ? b : c;
Run Code Online (Sandbox Code Playgroud)

现在,我们可以再次轻松编写Bounds方法:

private static double Bounds(double test, double? bottom, double? top) =>
  Middle(
    test, 
    bottom ?? Double.NegativeInfinity, 
    top ?? Double.PositiveInfinity);
Run Code Online (Sandbox Code Playgroud)

再次考虑一下我们在这里追求的目标。简单的方法可以做一件事情,并且做得非常好。一旦有了这些,就可以使用这些方法来构建其他简单的方法,并逐渐增加程序的复杂性而又不会让人难以理解。