我想我忘记了一些明显的事情,但如果它验证了尽可能保持干燥的条件,我似乎找不到一种分配值的方法
首先让我们消除您的一个常见误解。
这是对 DRY 含义的完全误解。如果你有一个Customer
对象并且你有一个Address
对象和Customer
有字段BillingCity
and BillingPostalCode
and HomeCity
等等,那么你的代码就不是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));
现在我们该怎么办?假设我们只想调用,则没有明显的方法可以在不用手写出来的情况下保留语义M()
if P()
一片空白。
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
and ID
成为具有特定含义但仅在范围内的只读变量in
:
N(P() ?? (let m = M() in m > 0 ? m : 0));
请注意,C#does支持let
表达但是仅在查询理解中。如果它能在语言中更普遍地支持它,那就太好了。
多年来,已经有许多建议添加let
多年来,表达式或其更通用的形式(排序表达式)被引入到 C# 中。有关示例,请参阅 github roslyn 问题跟踪器。也许这会进入 C# 8;如果你想要的话,就去参加论坛吧。
那么与此同时你能做什么呢?
原来在那里areC# 中的 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()
一次,但是超级奖金,它更容易阅读,因为现在程序文本表示对其执行的操作,而不是程序文本是一系列难以阅读的标点符号。
一些流畅风格的扩展方法可以让你的代码更容易阅读。养成使用它们的习惯。