在最近发布的DotNet&More Blazor播客,NetCore 3.0 Preview,C#8中,我们不仅随便提到了诸如C#8这样的热门话题。 有关使用C#8的经验的故事还不够大,无法单独解决,因此决定与它分享书信体裁的方式。
在本文中,我想谈谈我在生产环境中使用C#8的4个月的经验。 您可以在下面找到以下问题的答案:
- 如何在新的C#中“拼写”
- 哪些功能真正有用
- 什么令人失望
可以从Microsoft的官方文档中找到C#8功能的完整列表。 在本文中,我将忽略由于某种原因而无法尝试的那些机会,即:
- 只读成员
- 默认界面成员
- 一次性引用结构
- 异步流
- 指数和范围
我建议从一种在我看来似乎最美味的方式开始。
切换表达式
在我们的梦想中,我们非常乐观地呈现此功能:
int Exec(Operation operation, int x, int y) => operation switch { Operation.Summ => x + y, Operation.Diff => x - y, Operation.Mult => x * y, Operation.Div => x / y, _ => throw new NotSupportedException() };
但是,不幸的是,现实会自行调整。
首先,不可能合并条件:
string TrafficLights(Signal signal) { switch (signal) { case Signal.Red: case Signal.Yellow: return "stop"; case Signal.Green: return "go"; default: throw new NotSupportedException(); } }
实际上,这意味着在一半情况下,必须将switch表达式转换为常规switch,以避免复制粘贴。
其次,新语法不支持语句,即 不返回值的代码。 看起来似乎很好,并且没有必要,但是当我意识到在测试中使用断言(例如与模式匹配结合使用)的频率时,我自己感到惊讶。
第三,最后一段后面的switch表达式不支持多行处理程序。 在添加日志时,我们了解到多么可怕:
int ExecFull(Operation operation, int x, int y) { switch (operation) { case Operation.Summ: logger.LogTrace("{x} + {y}", x, y); return x + y; case Operation.Diff: logger.LogTrace("{x} - {y}", x, y); return x - y; case Operation.Mult: logger.LogTrace("{x} * {y}", x, y); return x * y; case Operation.Div: logger.LogTrace("{x} / {y}", x, y); return x / y; default: throw new NotSupportedException(); } }
我不想说新开关不好。 不,他很好,只是还不够好。
属性和位置模式
一年前,在我看来,他们似乎是“改变发展的机会”头衔的主要候选人。 而且,正如预期的那样,为了充分利用位置和财产模式的力量,您需要更改开发方法。 即,必须模仿代数数据类型。
看来是什么问题了:进入标记界面并继续。 不幸的是,这种方法在大型项目中有一个严重的缺点:没有人能保证在设计时跟踪您的代数类型的扩展。 因此,很可能随着时间的流逝,对代码的更改将在最意想不到的地方导致很多“默认失败”。
元组模式
但是事实证明,与样本进行比较的新可能性的“年轻兄弟”确实做得很好。 事实是,元组模式不需要在我们熟悉的代码架构中进行任何更改,它只是简化了一些情况:
Player? Play(Gesture left, Gesture right) { switch (left, right) { case (Gesture.Rock, Gesture.Rock): case (Gesture.Paper, Gesture.Paper): case (Gesture.Scissors, Gesture.Scissors): return null; case (Gesture.Rock, Gesture.Scissors): case (Gesture.Scissors, Gesture.Paper): case (Gesture.Paper, Gesture.Rock): return Player.Left; case (Gesture.Paper, Gesture.Scissors): case (Gesture.Rock, Gesture.Paper): case (Gesture.Scissors, Gesture.Rock): return Player.Right; default: throw new NotSupportedException(); } }
但是最好的部分是,此功能可以预知,可以与Deconstruct方法一起使用。 只需传递带有已实现的Deconstruct的类即可切换和使用元组模式的功能。
使用声明
这似乎是一个很小的机会,但是却带来了很多欢乐。 在所有促销活动中,Microsoft都在谈论减少嵌套的方面。 但是说实话,没有那么重要。 但是真正严重的是排除一个代码块的副作用:
- 通常,在添加using时,我们必须使用copy-paste方法将代码“放入”该块。 现在我们只是不考虑它
- 在using内部声明并在using对象的Dispose之后使用的变量确实让人头疼。 一个少的问题
- 在需要频繁进行Dispose调用的类中,每个方法长2行。 这看起来有些琐事,但是在许多小的方法的情况下,此琐事不允许在一个屏幕上显示足够数量的这些方法
结果,使用声明之类的简单操作极大地改变了编码的感觉,以至于您根本不想返回到c#7.3。
静态局部功能
老实说,如果没有代码分析的帮助,我什至不会注意到这种可能性。 尽管如此,她还是坚定地执行了我的代码:毕竟,静态局部函数非常适合小型纯函数的角色,因为它们无法支持方法变量的关闭。 这样一来,您就可以轻松上手,因为您知道代码中的潜在错误少了一个。
可空引用类型
对于甜点,我想提到C#8的最重要功能。 实际上,解析可为空的引用类型值得单独撰写。 我只想描述这些感觉。
总结
当然,所提供的机会并没有完全成熟,但是C#和F#/ Scala之间的差距越来越小。 无论好坏,时间都会证明一切。
在发布本文时,C#8可能已经纳入您的项目,所以我想知道您对我们最喜欢的语言的新版本有何看法?