虽然格式化 C# 代码的正确方法可能不尽相同,但在整个团队中达成一致的风格可以使代码库更整洁、可读性更高、可扩展性更强。一份条理清晰的风格指南可以帮助您控制差异,从而制作出具有凝聚力的最终产品。本页提供了在创建自己的样式指南时应牢记的命名约定和代码格式的提示和主要注意事项。
请注意:此处分享的建议基于微软提供的建议。最好的代码风格指南规则就是最符合团队需求的规则。
您可以在这里找到代码风格指南示例,或下载完整的电子书创建 C# 风格指南:编写可扩展的更简洁代码 。
由于 C# 使用空格符来分隔标识符,因此不能在变量名中定义带空格的变量。套码方案可以缓解在源代码中使用复合名称或短语的问题。
下面列出了几种著名的命名和套管约定:
骆驼皮箱
驼峰式也称骆驼帽,是指在书写短语时不使用空格或标点符号,用一个大写字母分隔单词。第一个字母是小写。
通常情况下,局部变量和方法参数使用驼峰字体。例如:
examplePlayerController
maxHealthPoints
endOfFile
帕斯卡案例
帕斯卡大小写是驼峰式大小写的一种变体,首字母大写。在 Unity 开发中用于类和方法名称。公共字段也可以采用帕斯卡模式。例如:
ExamplePlayerController
MaxHealthPoints
EndOfFile
蛇箱
在这种情况下,单词之间的空格用下划线字符代替。例如:
example_player_controller
max_health_points
end_of_file
烤肉串盒
在这里,单词之间的空格用破折号代替。然后,这些单词会出现在一种由破折号字符组成的 "串 "上。例如:
example-player-controller
最大健康点数
文件结束
烤肉串大小写的问题在于,许多编程语言将破折号用作减号。此外,有些语言将用破折号分隔的数字解释为日历日期。
匈牙利语符号
变量或函数的名称通常表示其意图或类型。例如:
int iCounter
string strPlayerName
匈牙利语符号是一种较早的约定,在 Unity 开发中并不常用。
在使用变量和字段时,请考虑这些规则:
- 变量名使用名词:变量名应清晰且具有描述性,因为它们代表了特定的事物或状态。除非变量的类型是 bool(见下文),否则在命名时请使用名词。
- 用动词作为布尔的前缀:这些变量表示真值或假值。它们通常是问题的答案,例如播放器在运行吗?游戏结束了吗?
- 给它们加上动词前缀,使其含义更加明显。通常会与描述或条件配对,例如,isDead、isWalking、hasDamageMultiplier 等。
- 使用有意义的名称。不要缩写(除非是数学):您的变量名将揭示其意图。选择易于发音和搜索的名称。
- 单字母变量适用于循环和数学表达式,但在其他情况下不要缩写。清晰比省略几个元音节省的时间更重要。
- 为了快速建立原型,可以暂时使用简短的 "垃圾 "名称,之后再重构为更有意义的名称。
- 公共字段使用 pascal 大小写,私有变量使用 camel 大小写:要替代公共字段,可使用带有公共 "getter "的属性(见上节和下节)。
- 避免过多的前缀或特殊编码:您可以在私有成员变量前加上下划线 (_) 以区别于局部变量。
- 或者,在上下文中使用 this 关键字来区分成员变量和局部变量,并跳过前缀。公共字段和属性一般没有前缀。
- 有些样式指南会为私有成员变量(m_)、常量(k_)或静态变量(s_)使用前缀,这样名称就能让人一目了然地了解变量的更多信息。
- 许多开发人员放弃了这些,转而依赖编辑器。不过,并非所有集成开发环境都支持高亮显示和彩色编码,有些工具根本无法显示丰富的上下文。在决定如何(或是否)作为一个团队共同应用前缀时,请考虑这一点。
- 统一指定(或省略)访问级别修改器:如果不使用访问修饰符,编译器会认为访问级别应为私有。这样做效果很好,但省略默认访问修饰符的方式要保持一致。
- 记住,如果以后要在子类中使用此功能,则需要使用 protected。
枚举是由一组命名常量定义的特殊值类型。默认情况下,常量为整数,从 0 开始向上计数。
枚举名和值使用 pascal 大小写。您可以将公共枚举置于类之外,使其成为全局枚举。枚举名称使用单数名词。
请注意:标有 FlagsAttribute的枚举是个例外。由于它们代表的类型不只一种,因此通常要将其复数化。
在命名类和接口时,请遵循这些标准规则:
班级名称使用大写名词:这将使您的课程井然有序。
如果文件中有 MonoBehaviour,源文件名必须匹配:文件中可能还有其他内部类,但每个文件只能有一个 MonoBehaviour。
以大写字母 "I "作为接口名称的前缀: 接着用一个形容词来描述功能。
在 C# 中,每一条执行指令都是在方法的上下文中执行的。
方法执行动作,因此要应用这些规则来相应地命名它们:
以动词作为名称的开头: 必要时添加上下文(如GetDirection、FindTarget 等)
参数使用驼峰字体: 将传入方法的参数格式化,就像本地变量一样。
返回 bool 的方法应提出问题: 与布尔变量本身一样,如果方法返回的条件是 "真-假",则在方法前加上动词。这种短语以问题的形式出现(如IsGameOver、HasStartedTurn)。
请注意:在 Unity 开发中,"函数 "和 "方法 "这两个术语经常交替使用。不过,在 C# 中,如果不将函数并入类,就无法编写函数,因此 "方法 "才是正确的术语。
C# 中的事件实现了观察者模式。这种软件设计模式定义了一种关系,在这种关系中,一个对象,即主体(或发布者),可以通知一系列称为观察者(或订阅者)的从属对象。因此,主体可以向其观察者广播状态变化,而无需将相关对象紧密耦合在一起。
主体和观察者中的事件及其相关方法有几种命名方案。尝试以下各节中的做法。
用动词短语为事件命名。请务必选择一个能准确传达状态变化的选项。
用现在分词或过去分词表示事件发生之前或之后的状态。例如,为开门前的事件指定 "OpeningDoor",为开门后的事件指定 "DoorOpened"。
使用 System.Action 委托处理事件。在大多数情况下, 动作<T委托可以处理游戏所需的事件。
最多可传递 16 个不同类型的输入参数,返回类型为 void。使用预定义委托可以节省代码。
请注意:您还可以使用 事件处理程序或 EventHandler<TEventArgs>。代表。作为一个团队,就每个人应如何实施活动达成一致。
在事件发生方式前加上 "On"。调用事件的主体通常是通过带 "On "前缀的方法(如 "OnOpeningDoor "或 "OnDoorOpened")来调用事件的。
在事件处理方法(在观察者中)前加上主体名称和下划线 (_)。如果主题名为 "GameEvents",那么你的观察者就可以有一个名为 "GameEvents_OpeningDoor "或 "GameEvents_DoorOpened "的方法。
为团队确定统一的命名方案,并在风格指南中实施这些规则。
请注意:这种 "事件处理方法 "不能与 EventHandler 委托相混淆。
仅在必要时创建自定义 EventArgs。如果您需要向事件传递自定义数据,请创建一种新的 EventArgs 类型,或者从 System.EventArgs或自定义结构体中继承而来。
使用命名空间可确保您的类、接口、枚举等不会与其他命名空间或全局命名空间中的已有类、接口、枚举等发生冲突。命名空间还可以防止与 Unity 资产商店中的第三方资产或其他测试场景发生冲突,因为这些资产或场景不会成为项目最终版本的一部分。
应用命名空间时
使用无特殊符号或下划线的 Pascal 大小写。
在文件顶部添加using指令,以避免重复键入命名空间前缀。
同时创建子命名空间。使用点(.)运算符来分隔脚本名称的层级,这样就可以将脚本按层级分类。例如,您可以创建 "MyApplication.GameFlow"、"MyApplication.AI"、"MyApplication.UI "等,以保存游戏的不同逻辑组件。
在代码中,这些类分别称为Enemy.Controller1和Enemy.Controller2。添加使用行,以节省键入前缀的时间(例如,使用 Enemy;)。
当编译器发现Controller1和Controller2 这两个类名时,它就会理解您的意思是Enemy.Controller1和Enemy.Controller2。
如果脚本需要引用不同命名空间的同名类,则使用前缀来区分它们。例如,如果在 Player 命名空间中有 Controller1 和 Controller2 类,则可以写出Player.Controller1 和Player.Controller2,以避免任何冲突。否则,编译器将报错。