清理代码如何创建自己的 C# 代码风格

尽管C#有不止一种编写风格,但只有统一编程风格,团队才能打造出简介、可读性和可扩展强的代码库。在这篇博客中,我们准备了一些编程准则和例子,让你能开发和维护自己的编程风格指南。
注意,以下仅为微软推荐而来的建议。你可以借此汲取灵感并为团队找到合适的风格。
理想情况下,一个Unity项目从感觉上应该由一个人开发的,不管真正的项目开发者有多少。风格指南有助于统一脚本方法、打造更一致的代码库。
如果可能,最好的做法就是遵循行业准则,以市面上已有的风格指南作为基础创立你自己风格。我们与内部和外部的 Unity 专家合作,发布了一本新的电子书,创建 C# 风格指南:根据微软全面的 C# 风格,编写更简洁、可扩展的代码 ,以获得灵感。
Google C# 风格指南是另一个很好的资源,可用于定义有关命名、格式化和注释约定的指南。当然,这里没有绝对正确或错误的做法,我们只是选择了微软的标准作为指南的基础。
我们的电子书和C# 示例文件均免费提供。 这两个资源都侧重于在 Unity 中开发时会遇到的最常见的编码规范。从本质上讲,这些都是微软框架设计指南的子集,其中包含了大量的最佳实践,超出了我们在本篇文章中所涉及的范围。
我们建议你根据团队的喜好来适当地定制风格指南中的准则。如果偏好与我们的推荐及Microsoft Framework Design指南产生了冲突,请以你的偏好为准。
制定风格指南需要不少的前期投资,但以后一定会带来不小的回报。比如,有一套统一的标准可以减少开发组熟悉新项目的时间。
当然,风格一致是好代码的关键。只要遵循了这些建议,如果你在未来希望修改代码风格,就可以直接用查找和替换操作来快速改写代码库。
我们建议制定一份涵盖大多数日常使用情况、符合自己需求的使用指南。不要从一开始就试着覆盖所有极端情况,以防指南过于臃肿。随着时间的推移,风格指南可以随着项目的迭代而有机地改进。
大多数风格指南都需要包含基本的格式要求。命名惯例、命名空间以及类的使用策略这些抽象概念可在以后慢慢完善。
让我们看看几种常见的格式和命名要求,为你自己的风格指南做参考。
C#两种常见的缩进风格分别为Allman风格和K&R风格,前者将大括号另起一行(也被称为BSD Unix的BSD风格),后者则“保持了一整个大括号”,将起头的大括号与上一条标题放在同一行。
为了提高可读性,我们根据Microsoft Framework Design指南挑选了Allman风格:
未知块类型 "codeBlock",请在 "serializers.type "道具中为其指定一个序列化器
无论选择什么风格,指南应该为团队的每位程序员所遵守。
指南还应指明嵌套语句的大括号是否应该保留。在下方例子中,尽管去掉大括号不会报错,但代码在被阅读时可能让人迷惑。因此我们仍旧建议加入大括号来让代码更加清楚明了。
未知块类型 "codeBlock",请在 "serializers.type "道具中为其指定一个序列化器
简单如水平间距等元素可以增强代码在屏幕上的呈现。虽然您的个人格式偏好可能各不相同,但以下是我们的风格指南中关于提高整体可读性的一些建议:
- 添加空格以降低代码密度:额外的空白可以在视觉上给人一种行间分隔的感觉
未知块类型 "codeBlock",请在 "serializers.type "道具中为其指定一个序列化器
- 在函数参数之间,逗号后使用一个空格。
未知块类型 "codeBlock",请在 "serializers.type "道具中为其指定一个序列化器
- 括号和函数参数后不要加空格。
未知块类型 "codeBlock",请在 "serializers.type "道具中为其指定一个序列化器
- 函数名称和括号之间不要使用空格。
未知块类型 "codeBlock",请在 "serializers.type "道具中为其指定一个序列化器
- 括号内避免空格。
未知块类型 "codeBlock",请在 "serializers.type "道具中为其指定一个序列化器
- 在流量控制条件前使用一个空格:在流量比较运算符和括号之间添加一个空格。
未知块类型 "codeBlock",请在 "serializers.type "道具中为其指定一个序列化器
- 比较运算符前后使用一个空格。
未知块类型 "codeBlock",请在 "serializers.type "道具中为其指定一个序列化器
变量通常代表了一种状态,所以其名称必须是清晰的描述性名词。对于必须表示真假值的变量,您可以在布尔型变量前加上动词。它通常是某个问题的答案,比如玩家在跑动吗(is the player running)?游戏结束了吗(is the game over)?在前面加上一个助动词来阐明其含义。用一段描述句或条件句来起名,如isPlayerDead、isWalking、hasDamageMultiplier等等。
而方法负责执行动作,较好的做法是以动词起头,视需要再添加上下文,如GetDirection、FindTarget等基于返回类型所起的名称。如果该方法的返回类型为bool,也可以用一个问句来起名。
类似于布尔变量,如果该方法返回的是一个真/假条件,同样可以用助动词作为前缀。比如IsGameOver, HasStartedTurn这种问句。
事件和事件句柄的命名上也存在几条惯例。在我们的风格指南中,我们用动词短语来命名事件,类似于方法。使用现在分词或过去分词表示 "之前 "或 "之后 "的事件。比如,指定门被打开前的事件为OpeningDoor,门被打开后的事件为DoorOpened。
我们还建议不要用缩写。尽管省去几个字母在短期内能让你感觉到生产力的提高,但现在的你可能还看得懂这些缩写,在一年后其他人(甚至你自己)可能完全无法理解其含义。变量名称应该表意且容易说出来。单字母变量对于循环和数学表达式来说是的确可行,但除此之外,你应该尽量避免缩写。准确地表达意思要比省略几个音节更重要。
并且,尽量在一行仅声明一个变量;这么做代码不会太紧凑,但正确性和可读性会更高。避免冗余名称。如果类叫做Player,你就不需要再创建PlayerScore或PlayerTarget之类的成员变量了。可以直接将其精简成Score或Target。
此外,还应避免过多的前缀或特殊编码。我们的指南中强调的一种做法是在私有成员变量前加上下划线 (_) 以区别于局部变量。其他的风格指南还建议添加多种前缀,比如私有成员变量(m_)、常量(k_)或静态变量(s_),借此在名称里透露更多信息。
不过,更好的做法是在接口名称前再加一个大写的“I”,然后用一个形容词来描述功能。您甚至可以在事件发生方法(主题)前加上 "On":调用事件的主体通常是通过一个前缀为 "On "的方法来调用事件的,例如 OnOpeningDoor 或 OnDoorOpened。
未知块类型 "codeBlock",请在 "serializers.type "道具中为其指定一个序列化器
Camel和Pascal是最常用的写法,而Snake、Kebab及Hungarian要更为少见。我们在指南中推荐用Pascal命名公共字段、枚举、类和方法,用Camel命名私有变量,这是Unity内部的普遍做法。
出了这里所提及的内容外,制定一份风格指南还要考虑到许多其他规则。示例指南和我们的新电子书创建 C# 样式指南:编写更简洁、可扩展的代码 ,提供更多更好的组织技巧。
我们清理代码的目的是用一套标准来提高开发工作的可扩展性。风格指南应该让开发者明确需要遵守的规则要求。你可以借助本指南建立编辑代码库的共同惯例,促进项目的商业化生产。
你也能根据自己的需求来确定风格指南的覆盖面大小。你的团队应当自行决定是否要为更抽象、不可捉摸的概念制定规则。它可以包含命名空间的使用规则、类的分解或#region等指令的应用于否。虽然 #region 可以帮助你折叠和隐藏 C# 文件中的代码部分,使大型文件更易于管理,但它也是许多开发人员认为是代码气味或反模式的一个例子。因此,你需要尽量避免在相关方面设立严格标准。并非所有东西都需要参考指南——有些东西只需团队讨论一下就可以了。
在与参与撰写的专家交谈时,他们大多认为代码的可读性高于一切。你可以参考以下指导意见来提高代码可读性:
- 减少使用参数:参数可以增加方法的复杂性。减少参数数量能使方法阅读和测试起来更容易。
- 避免过度超载:你可以生成无穷无尽的方法重载。请挑选几个能反映方法调用方式的参数来编写方法。并且,请保证过载方法的每次方法签写都有固定数量的参数,以防被混淆。
- 避免副作用一个方法只需要做它的名字所宣传的事情。避免修改任何超出其范围的内容。请尽可能用值而非引用来传递参数。并且,在方法通过out或ref关键词发回结果时,请确认它的确是你想让该方法完成的事。虽然副作用对某些任务很有用,但也可能导致意想不到的后果。编写一个没有副作用的方法来减少意外发生。

