【问题标题】:DRY (Don't Repeat Yourself) and if assignementsDRY(不要重复自己)和如果作业
【发布时间】:2018-11-04 21:32:43
【问题描述】:

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

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";

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

【问题讨论】:

  • 重复了什么?我在这里没有看到任何重复。
  • 为什么将方法的结果分配给变量是一件坏事?如果您不想重复方法调用,那就是这样做的方法
  • 我什至不知道您期望什么“更好的主意”。如果您想为a 分配一个新值,它必须已经是一个变量(或属性),而不是一个表达式。
  • 我认为您误解了 DRY 的实际含义。
  • 我唯一想到的是,不要使用条件运算符,而是:if(a <= 1) a = b;

标签: c# if-statement syntax dry


【解决方案1】:

我想我忘记了一些明显的东西,但如果它验证了尽可能保持 DRY 的条件,我似乎无法找到分配值的方法

让我们从消除您常见的误解开始。

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

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

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

我们经常处于“表达式上下文”中的情况——也就是说,一个长的、可能是流利的表达式,我们希望避免做多余的工作。例如,我们可能有:

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(),但是超级奖励,它更容易阅读,因为现在程序文本表示正在对其执行的操作,而不是程序文本是一堆难以-读标点符号。

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

【讨论】:

  • 喜欢它!很多信息,你清楚地明白了我的意思(这似乎并不容易)。关于 DRY,这就是我被告知的方式,我会去阅读更多关于它的信息。另外,当我谈到 DRY 时,我的意思实际上是可读性/代码分解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-03
相关资源
最近更新 更多