103
社区成员
发帖
与我相关
我的任务
分享| 这个作业属于哪个课程 | 2501_CS_SE_FZU |
|---|---|
| 这个作业要求在哪里 | 团队作业—Beta冲刺 |
| 这个作业的目标 | 相比alpha冲刺,构建更加系统,完整的代码规范 |
| 其他参考文献 | 命名准则(微软),代码风格(谷歌),《编写可读代码的艺术》,《Effective C#》,《构建之法(第三版)》 |
【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
【强制】代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
【强制】类名使用 UpperCamelCase 风格,必须遵从帕斯卡命名法。
正例:PlayerController, GameManager, UIManager
【强制】方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰命名法。
正例:playerHealth, MoveCharacter(), currentScore
【强制】常量命名全部大写,单词间用下划线隔开。
正例:MAX_HEALTH, DEFAULT_GRAVITY
【强制】Unity组件的私有字段使用驼峰命名法,建议添加SerializeField特性。
正例:
[SerializeField] private int playerHealth;
[SerializeField] private GameObject projectilePrefab;
【建议】布尔类型的变量命名应使用is、has、can等前缀。
正例:isAlive, hasWeapon, canJump
【强制】事件和委托使用适当的后缀。
正例:OnPlayerDeath, OnScoreChanged
【强制】使用Allman风格大括号,即左大括号换行。
【强制】即使只有一行代码,也要使用大括号。
正例:
if (isAlive)
{
Move();
}
反例:
if (isAlive)
Move();
【强制】采用 4 个空格缩进,禁止使用 Tab 字符。
【强制】单行字符数限制不超过 120 个,超出需要换行。
【强制】在Awake或Start中缓存频繁访问的组件引用,避免在Update中使用GetComponent。
正例:
private Rigidbody rb;
private void Awake()
{
rb = GetComponent<Rigidbody>();
}
【强制】理解并正确使用Unity的生命周期方法:
【强制】所有的方法和函数都应该以描述这段代码的功能的一段简明注释方法是干什么。描述不应包括它是怎么做的等具体细节,避免注释随代码变更而过时。
【强制】注释的内容要清楚、明了,含义准确,防止注释二义性。修改代码同时修改相应的注释,不再有用的注释要删除。
【强制】不允许在一行代码或表达式的中间插入注释。
【建议】源程序注释量尽量在30%以上。建议以一个系统内部模块作为单位进行检查。
【强制】公共方法和类必须使用XML文档注释。
正例:
/// <summary>
/// 处理玩家受伤逻辑
/// </summary>
/// <param name="damage">受到的伤害值</param>
public void TakeDamage(int damage)
{
// 实现
}
【建议】使用特性为Inspector字段添加说明。
正例:
[Header("玩家属性")]
[Tooltip("玩家的生命值")]
[SerializeField] private int health = 100;
【强制】每个类开头必须要有以下注释,位于 using 代码块之下。
正例:
using UnityEngine;
/// <summary>
/// 一句话简述
/// 作用:(对此类的详细描述)
/// 作者:(创建者的中文名字)
/// 编写日期:(模块创建日期,格式:YYYY-MM-DD)
/// 适用Unity版本:(2021.3.27f1c2 - 2022.3.3f1c1)
/// 脚本适用平台:(Android iOS)
/// 脚本使用注意事项:(例如:序列帧文件名称必须从0开始计数,即 图片名_0000 类似命名)
/// TODO:(还需要完成的功能)
/// </summary>
public class PlayerManager : MonoBehaviour
{
}
【强制】每个方法开头必须要有以下XML格式注释。
正例:
/// <summary>
/// 方法名称
/// 作用:(对这个方法的作用进行简单描述,包括所有参数的简单描述)
/// 作者:(方法创建者的中文名字)
/// 编写日期:(方法创建日期,格式:YYYY-MM-DD)
/// </summary>
/// <param name="_str1">参数_str1的详细说明</param>
/// <param name="_str2">参数_str2的详细说明</param>
/// <returns>返回值说明</returns>
/// <exception>异常说明</exception>
/// <remarks>
/// 该方法详细文字说明
/// </remarks>
/// <example>
/// <code>调用此方法的代码示例</code>
/// </example>
public int TestClass(string _str1, string _str2)
{
return 0;
}
【强制】其他人对代码做出修改后,要在“作者”一行中加入自己的名字。
【建议】在“作用”描述中,使用以下标记记录变更:
@since YYYY-MM-DD:表示从哪个日期开始,做出了什么样的修改或新功能。
@deprecated YYYY-MM-DD:表示从哪个日期开始,弃用了哪些功能。
@see 类名/方法名:表示相关的类或者方法。
@Override:表示重载父类的方法,并对重载进行文字说明。
正例:
/// <summary>
/// 方法名称
/// 作用:@since 2023-05-15 添加了对AB包进行二次加密和解密的功能
/// @deprecated 2023-05-17 弃用了之前过于简单的加密方式
/// @Override 对ResetCharacter方法进行的重载,这里是重置新添加的中立生物的状态
/// 作者:(方法创建者的中文名字)
/// 编写日期:(方法创建日期,格式:YYYY-MM-DD)
/// </summary>
【强制】类和方法中声明的变量、属性、字段,其上一行均要有 // 形式的注释说明。
正例:
// 说明这个变量的具体作用
[SerializeField] private string playerName = "abc";
【强制】在代码的功能、意图层次上进行注释,提供有用、额外的信息,而不是简单的代码翻译。
【强制】对关键变量的定义和分支语句(条件分支、循环语句等)必须编写注释。这些语句往往是程序实现某一特定功能的关键。
正例:
// 错误示例:如果 receiveFlag 为真
// 正确示例:如果从连结收到消息
if (receiveFlag)
{
// ...
}
【建议】注释应考虑程序易读及外观排版的因素。
【建议】使用的语言若是中、英兼有的,建议多使用中文,除非能用非常流利准确的英文表达。中文注释中需使用中文标点。
【建议】方法和类描述的第一句话尽量使用简洁明了的话概括一下功能,然后加以句号。
【建议】对于全局管理器使用单例模式,但要谨慎使用。
正例:
public class GameManager : MonoBehaviour
{
public static GameManager Instance { get; private set; }
private void Awake()
{
if (Instance == null)
{
Instance = this;
DontDestroyOnLoad(gameObject);
}
else
{
Destroy(gameObject);
}
}
}
【建议】使用基于委托的事件系统进行组件间通信,减少耦合。
正例:
public class EventManager : MonoBehaviour
{
public static event Action<int> OnScoreChanged;
public static void RaiseScoreChanged(int newScore)
{
OnScoreChanged?.Invoke(newScore);
}
}
【强制】避免在Update中实例化对象,使用对象池。
正例:
public class ProjectilePool : MonoBehaviour
{
[SerializeField] private GameObject projectilePrefab;
private Queue<GameObject> projectiles = new Queue<GameObject>();
public GameObject GetProjectile()
{
if (projectiles.Count > 0)
{
return projectiles.Dequeue();
}
return Instantiate(projectilePrefab);
}
}
【强制】妥善管理协程的生命周期,避免内存泄漏。
正例:
private Coroutine damageCoroutine;
private void StartDamageEffect()
{
if (damageCoroutine != null)
{
StopCoroutine(damageCoroutine);
}
damageCoroutine = StartCoroutine(DamageEffectRoutine());
}
【强制】标识符必须以字母或下划线(_)开头;可以包含 Unicode 字母、十进制数字、连接字符、组合字符或格式字符;可使用 @ 前缀声明逐字标识符(用于与关键字同名的标识符)。
示例:
// 合法标识符
int _count;
string name123;
var @class = "关键字作为标识符"; // 声明名为 class 的标识符
【重要】语言规范只允许特定 Unicode 类别(字母、数字、连接、组合、格式);其他字符会被替换为下划线,可能影响某些 Unicode 字符的使用。
【强制】标识符不应包含两个连续下划线(__),这是编译器保留的模式。
反例:
// 不应使用
int __generatedId; // 避免:与编译器生成标识符冲突
【建议】对类型(class、struct、enum、delegate)、命名空间和所有对外可见成员使用 PascalCase(UpperCamelCase)。接口以大写 I 前缀。
示例:
public class PlayerController { }
public interface IWorkerQueue { }
public struct ValueCoordinate { }
public delegate void Notify(string message);
namespace CompanyName.GameName.Gameplay { }
【建议】方法、属性与事件使用 PascalCase。
示例:
public void StartGame() { }
public int MaxPlayers { get; set; }
public event Action OnPlayerDeath;
【强制】方法参数和局部变量使用 camelCase(lowerCamelCase)。
示例:
public void ProcessData(int itemCount, bool isActive)
{
int currentIndex = 0;
}
【强制】私有实例字段使用下划线前缀 + 驼峰(camelCase)。对于静态字段可使用 s 前缀;线程静态字段可使用 t_ 前缀(项目可选约定)。
示例:
private IWorkerQueue _workerQueue;
private static TimeSpan s_cachedTimeout;
[ThreadStatic] private static int t_threadId;
【强制】规定常量使用全大写与下划线分隔
示例:
public const int MAX_HEALTH = 100;
private const string DEFAULT_NAME = "Player";
【建议】泛型类型参数使用描述性名称,单字母名称仅在自我说明时允许(常用 T 或 TValue 等)。对于描述性名称,前缀使用 T
示例:
public interface ISessionChannel<TSession> { TSession Session { get; } }
public delegate TOutput Converter<TInput, TOutput>(TInput input);
public class Cache<TItem> { }
【强制】对于 class 和 struct 主构造函数参数使用 camelCase;对于 record 主构造参数使用 PascalCase(因为会成为公共属性)。
示例:
public class DataService(IWorkerQueue workerQueue, ILogger logger) { }
public struct Point(double x, double y) { }
【强制】使用 EditorConfig、代码样式规则或 IDE 的命名规则来在团队中强制命名约定。
【建议】使用字符串内插(interpolation)连接短字符串以提高可读性,而不是使用连接运算符或 String.Format。
正例:
string displayName = $"{nameList[n].LastName}, {nameList[n].FirstName}";
【强制】在循环中或处理大量文本时,使用 System.Text.StringBuilder 以避免大量短字符串拼接带来的性能开销。
正例:
var phrase = "lalalalalalalalalalalalalalalalalalalalalalalalalalalalalala";
var manyPhrases = new StringBuilder();
for (var i = 0; i < 10000; i++)
{
manyPhrases.Append(phrase);
}
// Console.WriteLine(manyPhrases.ToString());
【建议】优先使用原始字符串字面量(raw string literal)来表达跨行或包含大量转义字符的文本。
正例:
var message = """
This is a long message that spans across multiple lines.
It uses raw string literals. This means we can
also include characters like \n and \t without escaping them.
""";
【建议】使用基于表达式的字符串内插,而不是位置字符串内插(避免使用 {0}、{1} 等)。
正例:
Console.WriteLine($"{student.Last} Score: {student.score}");
【强制】记录类型(record)的主构造函数参数使用 PascalCase(因为会成为公共属性)。
正例:
public record Person(string FirstName, string LastName);
【强制】类和结构体的主构造函数参数使用 camelCase。
正例:
public class DataService(IWorkerQueue workerQueue, ILogger logger) { }
public struct Point(double x, double y) { }
【建议】优先使用 required 属性而非过多构造函数来强制初始化属性值(适用于 .NET 版本支持 required 的情况)。
正例:
public class LabelledContainer<T>(string label)
{
public string Label { get; } = label;
public required T Contents { get; init; }
}
【建议】使用集合表达式或集合初始化器来初始化集合,使意图清晰。
正例:
string[] vowels = [ "a", "e", "i", "o", "u" ];
// 或
var list = new List<int> { 1, 2, 3, 4 };
【建议】优先使用泛型委托 Func<> 和 Action<>,并使用 lambda 表达式或方法组简洁创建实例。
正例:
Action<string> actionExample1 = x => Console.WriteLine($"x is: {x}");
Func<string, int> funcExample1 = x => Convert.ToInt32(x);
actionExample1("string for x");
Console.WriteLine($"The value is {funcExample1("1")}");
【建议】仅在确有必要时才定义自定义 delegate 类型;使用方法组语法实例化委托更简洁。
正例:
public delegate void Del(string message);
public static void DelMethod(string str)
{
Console.WriteLine($"DelMethod argument: {str}");
}
Del exampleDel2 = DelMethod; // 方法组简化语法
exampleDel2("Hey");
【强制】只捕获并处理你能正确处理的特定异常类型,避免捕获 System.Exception。
正例:
static double ComputeDistance(double x1, double y1, double x2, double y2)
{
try
{
return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
catch (System.ArithmeticException ex)
{
Console.WriteLine($"Arithmetic overflow or underflow: {ex}");
throw;
}
}
【强制】如果 finally 块唯一用途是调用 Dispose,请改用 using 语句(含新的 using 声明语法)。
正例:
using (Font arial = new Font("Arial", 10.0f))
{
byte charset2 = arial.GdiCharSet;
}
// 或使用不带大括号的新语法(简洁,作用域为当前块):
using Font normalStyle = new Font("Arial", 10.0f);
byte charset3 = normalStyle.GdiCharSet;
【强制】在逻辑比较中使用短路运算符 && 和 ||,避免使用 & 和 |(除非确实需要按位或总是求值的行为)。
正例:
if ((divisor != 0) && (dividend / divisor) is var result)
{
Console.WriteLine($"Quotient: {result}");
}
else
{
Console.WriteLine("Attempted division by 0 ends up here.");
}
【建议】当变量声明的类型与构造对象类型一致时,使用简洁的 new() 语法或 var + new。
正例:
var firstExample = new ExampleClass();
ExampleClass instance2 = new();
ExampleClass secondExample = new ExampleClass();
【建议】优先使用对象初始化器来简化属性赋值。
正例:
var thirdExample = new ExampleClass
{
Name = "Desktop",
ID = 37414,
Location = "Redmond",
Age = 2.3
};
【建议】使用 lambda 表达式定义短小且无需注销的事件处理器;对于需要后续注销的处理器,使用具名方法以便移除。
正例(无需移除):
this.Click += (s, e) =>
{
MessageBox.Show(((MouseEventArgs)e).Location.ToString());
};
【强制】通过类型名调用 static 成员(ClassName.StaticMember),不要使用派生类名来限定基类中定义的静态成员。这样能保持可读性并避免未来潜在问题。
【建议】为查询变量使用有意义的名称;为匿名类型属性使用 PascalCase,并在有歧义时重命名属性以提高可读性。
正例:
var seattleCustomers = from customer in Customers
where customer.City == "Seattle"
select customer.Name;
var localDistributors2 =
from customer in Customers
join distributor in Distributors on customer.City equals distributor.City
select new { CustomerName = customer.Name, DistributorName = distributor.Name };
【建议】在查询和范围变量中使用隐式类型(var),尤其是当结果为匿名类型或复杂泛型时。
【强制】当变量右侧类型明显时使用 var(例如 new、字面量或显式转换);当类型不明显时使用显式类型。
正例:
var message = "This is clearly a string.";
var currentTemperature = 27;
var phrase = "lalala";
for (var i = 0; i < 10000; i++) { ... }
反例(类型不明显时不要使用 var):
var currentMaximum = ExampleClass.ResultSoFar(); // 不推荐,类型不明确
【强制】在 foreach 循环中显式声明循环变量类型,除非集合元素类型非常明显。
正例:
foreach (char ch in laugh)
{
// ...
}
【建议】多数代码文件应使用文件作用域命名空间声明以减少嵌套缩进。
正例:
namespace MySampleCode;
【强制】将 using 指令放在命名空间声明之外,避免引入上下文相关的名称解析问题。
正例:
using Azure;
namespace CoolStuff.AwesomeFeature
{
public class Awesome
{
public void Stuff()
{
WaitUntil wait = WaitUntil.Completed;
}
}
}
【强制】使用四个空格缩进。 不要使用选项卡(Tab)。
【建议】一致地对齐代码以提高可读性。
【建议】将行限制为 65 个字符,以增强文档上的代码可读性,尤其是在移动屏幕上。通过将长语句分解为多行来提高清晰度和用户体验。
【强制】对大括号使用“Allman”样式:左和右大括号另起一行。 大括号与当前缩进级别对齐。
正例:
if (isActive)
{
ProcessData();
}
【建议】如有必要,应在二元运算符之前换行。
正例:
var totalScore = currentScore
+ bonusPoints
+ levelMultiplier;
【建议】使用单行注释(//)以进行简要说明。避免使用多行注释(/* */)来进行较长的解释。
【强制】若要描述方法、类、字段和所有公共成员,请使用 XML 注释。
【强制】将注释放在单独的行上,而非代码行的末尾。
【强制】在注释分隔符 (//) 与注释文本之间插入一个空格。
【建议】注释文本首字母大写,并以句点结束(针对英文注释)。
正例:
// The following declaration creates a query. It does not run
// the query.
var query = from item in collection select item;
3.3.3 布局约定
【建议】使用默认的代码编辑器设置(智能缩进、4 字符缩进、制表符保存为空格)。
【强制】每行只写一条语句。
正例:
// 正确
count++;
name = "Unity";
// 错误
count++; name = "Unity";
【强制】每行只写一个声明。
正例:
// 正确
int x;
int y;
// 错误
int x, y;
【强制】如果连续行未自动缩进,请将它们缩进一个制表符位(四个空格)。
【建议】在方法定义与属性定义之间添加至少一个空白行。
【建议】使用括号突出表达式中的子句,除非是解释运算符或表达式优先级的情况。
正例:
if ((startX > endX) && (startXif ((startX > endX) && (startX > previousX))))
{
// Take appropriate action.
}
【强制】按照功能模块组织项目结构。
正例:
Assets/
├── Scripts/
│ ├── Core/ # 核心系统
│ ├── Gameplay/ # 游戏逻辑
│ ├── UI/ # 用户界面
│ ├── Utilities/ # 工具类
│ └── Managers/ # 管理器
├── Prefabs/
├── Scenes/
├── Art/
└── Audio/
【建议】使用命名空间组织代码结构。
正例:
namespace CompanyName.GameName.Gameplay
{
public class PlayerController : MonoBehaviour
{
// 实现
}
}
[建议]
提交模板
[类型] 类型关键字
[标题] 此次提交是进行哪些改动简述
[描述] 对此次提交进行详细的文字描述
[Bug修复情况] 此行为可选填,下面会说明如何填写
提交类型,在提交更改时,第一行要写的关键字,可以较快的辨别该提交是那种类型的
提交类型可以有多个,比如修复BUG是修改了UI模型,那么[类型]就是fix UI,两个类型之间用英文半角空格进行间隔
BUG修复情况说明,如果此次提交为修复BUG,要写明之前的BUG是什么样子的,例如网络访问错误还是人物操作抖动等,如果是修改issue的话,要引用该issue
[强制]在每次提交后与组内成员通知,并做好记录
【建议】使用Addressable Assets系统进行资源管理,避免Resources文件夹的滥用。
【强制】在合适的时机卸载不再使用的资源,避免内存泄漏。
【建议】使用ScriptableObject存储游戏配置数据。
正例:
[CreateAssetMenu(fileName = "GameSettings", menuName = "Game/Game Settings")]
public class GameSettings : ScriptableObject
{
public float gameTime = 60f;
public int maxPlayers = 4;
}
在设计和编写代码时,需要保护和限制代码对资源的访问权限,尤其是在使用或调用未知来源的代码时。
【强制】请勿使用代码访问安全性(CAS)。
【强制】请勿使用部分受信任的代码。
【强制】请勿使用 AllowPartiallyTrustedCaller 属性(APTCA)。
【强制】请勿使用 .NET 远程处理。
【强制】请勿使用分布式组件对象模型(DCOM)。
【强制】请勿使用二进制格式化程序。
【建议】不要加载和执行未知来源的代码,除非采取了替代安全措施。
未来不支持将代码访问安全性和安全透明代码用作部分信任代码的安全边界。
在设计和编写代码时,需要保护和限制代码对资源的访问权限,尤其是在使用或调用未知来源的代码时。
【强制】请勿使用代码访问安全性(CAS)。
【强制】请勿使用部分受信任的代码。
【强制】请勿使用 AllowPartiallyTrustedCaller 属性(APTCA)。
【强制】请勿使用 .NET 远程处理。
【强制】请勿使用分布式组件对象模型(DCOM)。
【强制】请勿使用二进制格式化程序。
【建议】不要加载和执行未知来源的代码,除非采取了替代安全措施。
未来不支持将代码访问安全性和安全透明代码用作部分信任代码的安全边界。
【建议】使用以下替代安全措施:
【强制】使用适当的日志级别:
【建议】在发布版本中移除或禁用不必要的日志输出。
【建议】为核心游戏逻辑编写单元测试,使用Unity Test Framework。
代码质量与规范
using 引用。PascalCase,参数/局部变量使用 camelCase,常量使用 UPPER_CASE。int, string而非 CLR 类型Int32, String。new 或显式转换时使用 var,否则显式声明类型。逻辑与性能
$,循环或大量拼接必须使用 StringBuilder。&& 和 ||。System.Exception;使用 using 语句块自动释放非托管资源。架构与设计
private,仅在必要时公开public/internal。架构设计
性能优化
Update 中 Instantiate 或 new 对象。FixedUpdate 中。UniTask 或 Invoke。内存管理
生命周期与组件
Awake 或 Start 中缓存组件引用GetComponent,严禁在 Update 中获取组件或查找对象Find。[SerializeField] private,避免直接使用 public 破坏封装性。OnDestroy 中移除事件监听、停止协程并清理非托管资源。架构与资源
Action, Func或消息中心进行组件间通信,减少强引用耦合。Resources 文件夹,建议使用 Addressables 或 AssetBundle;确保及时卸载未引用资源。Debug.Log 时注意级别,确保发布版本(Release)中剥离或禁用不必要的日志输出。ScriptableObject 存储,而非硬编码在脚本中。