读书笔记 - 改善 C# 程序的157个建议(部分)

8/16/2018 csharp

《改善 C# 程序的157个建议》读书笔记
第三部分:编码规范及习惯;
[命名规范] [代码整洁] [规范开发行为]

标 [+] 的为需要注意的,或可以关注的;
标 [-] 的为个人不赞同的;

[+] 指示的是其下方的建议项。

# 第三部分 编码规范及习惯

# 第10章 命名规范

# 1. 命名空间与程序集 命名方式
  • 公司组织名称

<Company>.<Component>


[+] * 使用域名(Java世界中常见)

Com.Microsoft.<Component>

命名空间与程序集没有必然关系,
命名空间是逻辑分组;
程序集是物理分组;

# 2. 对于编译成 DLL 的程序集,建议命名空间与程序集名称一致。

[+] ##### 3. 命名空间使用复数 另外,不要添加无意义前缀或后缀。 建议:System.Collections 不建议:System.Collection, System.AllCollections
# 4. 避免与 BCL 和 FCL 命名重复

BCL (Base Class Library)
FCL (Framework Class Library)


[+] ##### 5. 类型命名:名词或名词词组 类型是现实中的实际对象,是名词。

动词:对象的行为,而不是类型本身。


[+] ##### 6. 接口命名:形容词组 接口:Can Do; 表示具备某种能力。
# 7. 派生类使用基类名字做后缀

XxxException : Exception XxxAttribute : Attribute XxxEventArgs : EventArgs

# 8. 泛型使用 T 做前缀
# 9. 枚举

枚举类型:复数
枚举元素:单数

# 10. 公开元素:PascalCasing 命名
# 11. 考虑用类名作为属性名

public Company Company {get;set;}

# 12. 私有或局部变量:camelCasing 命名
# 13. 有条件地使用前缀

member : m_ static : s_ 在变量较多时,可以谨慎考虑使用。
不过,变量较多时,考虑重构。


[+++] ##### 14. 使用肯定性的短语命名布尔类型 前缀:Is Can Has ... 好:IsChecked 坏:Checked
# 15. 使用后缀表示已有类型的新版本。

X509Certificate
X509Certificate2

不得不这样时,加后缀,而不是前缀,便于被发现。


[+] ##### 16. 委托与事件*类型*应添加上后缀 XxxDelegate XxxCallback XxxEventHandler
[+] ##### 17. 委托和事件*变量*使用动词或形容词短语命名 Click SizeChanged 表示动作(委托)或事件的发生,状态的变化(事件)
[+] ##### 18. 事件处理器(事件的订阅函数)或委托处理器命名 组合: 事件变量所属对象_事件变量名 委托变量所属对象On委托变量名

好:NameOnPropertyChanged 坏:NameChanged (看起来像事件的变量名)

# 第11章 代码整洁

# 1. 使用默认的访问修饰符

不是很赞同。 原因:添加默认的修饰符,如 internal class,是在显式声明这个类不应该被变成 public 的, 而不是忘记添加访问修饰符了。可以一定程度上防止他人随意修改访问权限。


[+++] ##### 2. 不知道该不该用大括号,就用。 增加代码可读性。

下面的例子中,Resharper 等工具会提示 else 无需书写,以减少代码层级,但在这种情况下, 建议可以加上 else {},因为代码可读性明显提示。代码之间的关系和条理更清楚。

private bool Check()
{
    if(xxx)
    {
        // ...
        return true;
    }
    else
    {
        // ...
        return false;
    }
}
# 3. 总是使用有意义的命名。

同时,避免一心二意,同一个变量,在一段代码中应只表示一个含义。


[++] ##### 4. 方法的抽象级别应在同一层级。

坏:

public void Init()
{
    // xxx
    RemoteInit();
}

这个例子中,Init 方法先进行了部分初始化工作,然后调用 RemoteInit 完成另一部分初始化工作。

好:

public void Init()
{
    LocalInit();
    RemoteInit();
}

建议的做法是,将两部分初始化工作都抽象为相同层级的方法(LocalInit 和 RemoteInit),在 Init 中调用。 层级更清晰。

# 5. (单一职责)一个方法只做一件事。

事情可大可小,但方法过长过负责时,就要考虑拆分。

# 6. (单一职责)避免过长的方法和过长的类。

过长的方法和过长的类是重构的首选目标。

# 7. (高内聚,最小知识)只对外公布必要的操作。
# 8. 重构多个相关属性为一个类。

e.g. 如一个 Person 类中,有地址,邮编,邮箱,电话,手机号等等属性,就应该考虑将其抽象为联系方式一个类。

# 9. 不重复代码

[+] ##### 10. 使用表驱动法避免过长的 if 和 switch 分支。 使用字典、数组、索引等代替 if 和 switch 。 这个一种设计思路哦。
# 11. 使用匿名方法,Lambda 表达式代替方法。
# 12. 使用事件访问器替换公开的事件成员变量。

这个,C# 已经自动实现啦,无需考虑。


[-] ##### 13. 最少,甚至是不要注释。 这个,不赞同。 写了注释不能成为代码本身糟糕的借口,但不代表不需要写注释,尤其是公开的方法,复杂的逻辑。 建议:保持代码本身的优雅,但不过分追求精简注释。
# 14. 如抛出异常,则必须要注释。

方法内部会抛出异常,在方法签名上方需要写注释。


/// <Summary>
/// 注释
/// </Summary>
/// <exception cref="System.IO.IOException">什么情况下会抛出该异常</exception>
public void Xxx()
{
    // ...
    throw new IOException("XXX");
}

# 第12章 规范开发行为


[+++] ##### 1. 避免过度设计,在敏捷中重构 瀑布式开发:分析->设计->实现->测试。(试用与类似于建筑这样的"不可重构"的行业)缺点:不能很好地应对变化。 敏捷开发:关键词——迭代。(Spring) 敏捷使用“用户故事(User Story)”来核定需求和工作量。一个迭代提供一个完成的用户故事的实现,交付给客户(真实客户,或者测试部门,运营等。)
[+++] ##### 2. 单元测试 * TDD * 使用单元测试框架
# 3. 利用特性为应用程序提供多个版本

e.g. 条件编译 [Conditional("ONLINE")] [Conditional("OFFLINE")]

# 4. 界面的自动化测试

工具:Code UI Automation


END

更新时间: Friday, March 12, 2021 22:54