DRY (Don't Repeat Yourself) 和作业

DRY (Don't Repeat Yourself) and if assignements

我想我忘记了一些明显的东西,但我似乎无法找到一种方法来分配一个值,如果它验证一个条件保持尽可能干燥...... 一些代码来解释我的意思...

a = (b > 1) ? b : c;

甚至a = (a > 1) ? a : b;

所以当然这没什么大不了的,但如果 a 被方法调用替换,(可能是 yield return 那里)或其他什么,我将不得不调用它两次......

我唯一看到的是将它存放在一个变量中,然后就像上面的代码一样...

有更好的主意吗?

编辑以便更好地理解: 假设我正在 xml 文件中搜索一个值,使用空检查 (?. ?[]) 等等,比如

string store_no = myXmlNode.SelectSingleNode("aChildNode")?.SelectSingleNode("myNode")?.Attributes?["store_no]?.Value;

所以这里我将其存储在一个变量中,以便稍后测试它的值。 如果我想检查特定的 store_no !我将不得不做类似

的事情
store_no = (store_no=="STORE23")? store_no : "unknown";

...是的,不确定这个例子是否足够明确,但想法就在这里;我可能不想将数据存储在变量中(例如巨大的数据块)有没有办法获得相同的结果?

I think I'm forgetting something evident but I can't seem to find a way to assign a value if it validates a condition remaining as DRY as possible

让我们首先消除您对一个常见误解的误解。

这完全是对 DRY 含义的错误表述。 如果您有一个 Customer 对象并且您有一个 Address 对象并且 Customer 有字段 BillingCityBillingPostalCodeHomeCity 等等,那么你的代码不是 DRY 因为相同的信息在两个地方被冗余表示。您应该重新设计代码,使 Customer 具有 Address 个对象的集合。

现在,避免在整个节目中剪切和粘贴重复代码确实是个好主意,但 DRY 是关于代码的设计在大中型. DRY 绝对不意味着你的代码不应该在同一个表达式中两次使用同一个变量!

既然我们已经解决了这个问题,让我们看看您对语言的评论。

我们经常处于 "expression context" 的情况下——也就是说,一个很长的、可能是流畅的表达式,我们希望避免做多余的工作。例如,我们可能有:

x = M() > 0 ? M() : 0;

也许调用 M() 两次很昂贵,或者它不是幂等的。任何。没关系。重点是,我们不想调用它两次。

令人恼火的是我们必须退出表达式上下文并进入语句上下文:

var m = M();
x = m > 0 ? m : 0;

这当然是合法的,但是有点麻烦。此外,在某些情况下它可能会很棘手:

N(P() ?? (M() > 0 ? M() : 0));

现在我们该怎么办?假设我们只想在 P() 为 null 时调用 M(),则没有明显的方法可以在不手写的情况下保留语义。

var t = default(T);
var p = P();
if (p == null) {
  var m = M();
  t = m > 0 ? m : 0;
} else  {
  t = p.Value;
}
N(t);

恶心。天哪,太可怕了。

其他语言通过引入 let 表达式解决了这个问题。我们真正想要的是能够在表达式中间引入一个新的局部变量。一个常见的语法是 let ID = EXPRESSION in EXPRESSION 并且 ID 成为一个只读变量,具有特定含义但仅在 in:

范围内
N(P() ?? (let m = M() in m > 0 ? m : 0));

请注意,C# 支持 let 表达式,但 仅在查询理解中 。如果它在语言中更普遍地支持它,那就太好了。

这些年来,有许多提议将 let 表达式或它们更通用的形式(排序表达式)添加到 C# 中。有关示例,请参阅 github roslyn 问题跟踪器。也许这会进入 C# 8;想要的话就去参加论坛吧

那么您在此期间可以做什么?

原来 是 C# 中的 let 表达式。 let x = y in z 只是写 (((Func<X, Z>)(x=>z))(y)) 的好方法。所以你可以写:

N(P() ?? (((Func<int, int>)(m => m > 0 ? m : 0))(M())));

但这看起来几乎一样可怕。那是一个不可读的混乱。

问题是 lambda。这样会更好:

Func<int, int> f = m => m > 0 ? m : 0;
...
N(P() ?? f(M()));

但这有点不透明。我们如何进一步改进?

我们可以将其设为本地函数,但更好的是,我们可以将其设为扩展方法并进行流畅的编程:

public static int NotNegative(this int x) => x > 0 ? x : 0;
...
N( P() ?? M().NotNegative());

完成。这只计算了一次M(),但是超级奖励,它更容易阅读,因为现在程序文本代表正在对其执行的操作,而不是程序文本是一个转换难以阅读的标点符号。

一些流畅的扩展方法可以使您的代码更易于阅读。养成使用它们的习惯。