IDesign CSharp 编码规范 - 中文译版

45
C# 编编编编 准准准准准准准 原原原原: Juval Lowy 原原原原: 2.32 原原原原: www.idesign.net 原原原原: 编编编编编编编编编 原原原原: 准准准准 原原原原: 0.1 原原: 编编 原原: 2009/1/23

Transcript of IDesign CSharp 编码规范 - 中文译版

Page 1: IDesign CSharp 编码规范 - 中文译版

C# 编码规范准则和最佳实践

原文作者: Juval Lowy

原文版本: 2.32

获取原文: www.idesign.net

版权信息: 请参见原文版权信息

译文作者: 发条兔子

译文版本: 0.1

状态:

日期: 2009/1/23

Page 2: IDesign CSharp 编码规范 - 中文译版

修订记录

时间 作者 内容2009 年 1 月 23 日 发条兔子 ([email protected]) 翻译初版

Page 3: IDesign CSharp 编码规范 - 中文译版

译者序

IDesign 发布的 C# 编码规范是可以免费获取和使用的编码规范中比较完整的一个。本人多年在项目中一直使用。当然编码规范没有绝对的正确,也要视项目和开发人员的情况来合理的剪裁。但至少 IDesign 这个规范给了我们一个剪裁的基础吧,非常感谢他们做出的贡献。

由于时间原因,主要翻译了前三章,希望以后有时间再完成其余的部分。有些译文不是太有把握,所以将原文标记为高亮,请参考原文阅读。由于本人平时比较少做翻译工作,翻译的功底比较差。如果有不合适的地方欢迎发邮件到

[email protected] 指正,请在邮件中注明原文版本和译文版本(见封面)。

发条兔子2009 年 1 月

Page 4: IDesign CSharp 编码规范 - 中文译版

作者序

A comprehensive coding standard is essential for a successful product delivery. The standard helps in enforcing best practices and avoiding pitfalls, and makes knowledge dissemination across the team easier. Traditionally, coding standards are thick, laborious documents, spanning hundreds of pages and detailing the rationale behind every directive. While these are still better than no standard at all, such efforts are usually indigestible by the average developer. In contrast, the C# coding standard presented here is very thin on the “why” and very detailed on the “what” and the “how.” I believe that while fully understanding every insight that goes into a particular programming decision may require reading books and even years of experience, applying the standard should not. When absorbing a new developer into your team, you should be able to simply point him or her at the standard and say: "Read this first." Being able to comply with a good standard should come before fully understanding and appreciating it that should come over time, with experience. The coding standard presented next captures best practices, dos and don'ts, pitfalls, guidelines, and recommendations, as well as naming conventions and styles, project settings and structure, and framework-specific guidelines. Since I first published this standard for C# 1.1 in 2003, it has become the de-facto industry standard for C# and .NET development.

一个完善的编码规范对项目的成功是必不可少的。编码规范帮助整个团队遵循最佳实践和避免常见的错误,使团队更容易分享经验和知识。传统的编码规范往往是厚厚的数百页的文档,详细讲述每个细节和理论。虽然它们通常很难被一般的开发人员理解和消化,但总是比没有要好。相比来说,这个 C# 编码规范主要关注于“做什么”和“怎么做”,而对于“为什么”讲得比较少。我相信要完全理解和决定一个编码标准需要非常丰富的经验,而实施编码标准却没这个必要。当一个成员加入你的团队,你只需要让他(她)先阅读这个编码标准就可以了。要完全的认识和理解编码标准则需要一些时间和经验。这个编码规范包含了一些最佳实践,什么该做,什么不该做,容易犯的错误,一些准则和建议,已经命名规范,代码风格,项目设置和结构,框架准则。自从我在 2003 年为 C# 1.1 发布的第一个版本开始,它已经是业界比较公认的 C# 和 .NET 开发的标准了。

Juval Lowy2008 年 1 月

Page 5: IDesign CSharp 编码规范 - 中文译版

目录

修订记录.....................................................................................................................................................2

译者序.........................................................................................................................................................3

作者序.........................................................................................................................................................4

目录.............................................................................................................................................................5

1. Naming Conventions and Style 命名规范和代码风格............................................................................6

2. Coding Practices 编码实践....................................................................................................................11

3. Project Settings and Project Structure 项目设置和项目结构...............................................................22

4. Framework Specific Guidelines 框架代码准则.....................................................................................28

4.1. Data Access 数据访问....................................................................................................................28

4.2. ASP.NET and Web Services.............................................................................................................28

4.3. Multithreading 多线程...................................................................................................................30

4.4. Serialization 序列化........................................................................................................................33

4.5. Remoting........................................................................................................................................34

4.6. Security...........................................................................................................................................34

4.7. System.Transactions.......................................................................................................................36

4.8. Enterprise Services.........................................................................................................................36

5. Resources..............................................................................................................................................38

5.1. Programming .N ET Components 2nd Edition..................................................................................38

5.2. The IDesign Serviceware Downloads..............................................................................................38

5.3. The Architect’s Master Class...........................................................................................................38

Page 6: IDesign CSharp 编码规范 - 中文译版

1. Naming Conventions and Style 命名规范和代码风格

1. Use Pascal casing for type and method names and constants:

类型、方法和常量使用 Pascal 风格。

public class SomeClass

{

const int DefaultSize = 100;

public void SomeMethod()

{ }

}

2. Use camel casing for local variable names and method arguments.

局部变量和方法参数使用 Camel 风格。

void MyMethod(int someNumber)

{

int number;

}

3. Prefix interface names with I

接口使用 I 前缀。

interface IMyInterface

{…}

4. Prefix private member variables with m_. Use Pascal casing for the rest of a member variable name following the m_.

私有变量使用 m_ 前缀和 Pascal 风格。

public class SomeClass

{

private int m_Number;

}

5. Suffix custom attributes classes with Attribute.

自定义的属性类名使用 Attribute 后缀。6. Suffix custom exception classes with Exception.

自定义的异常类名使用 Exception 后缀。7. Name methods using verb-object pair, such as ShowDialog().

方法名使用动宾短语,如 ShowDialog() 。

Page 7: IDesign CSharp 编码规范 - 中文译版

8. Methods with return values should have a name describing the value returned, such as GetObjectState().

对于有返回值的方法,应该使用描述方法返回值的方法名,如 GetObjectState() 。9. Use descriptive variable names.

使用有意义的变量名。a. Avoid single character variable names, such as i or t. Use index or temp instead.

不要使用单个字符的变量名,如 i 或 t 。应使用 index 或 temp 。b. Avoid using Hungarian notation for public or protected members.

公共和保护的成员不要使用匈牙利命名法。c. Do not abbreviate words (such as num instead of number).

不要缩写单词,比如 number 缩写成 num 。10. Always use C# predefined types rather than the aliases in the System namespace.

总是使用 C# 预定义的类型,而不是 System 命名空间中的别名。如:

使用 object 不使用 Object

使用 string 不使用 String

使用 int 不使用 Int32

11. With generics, use capital letters for types. Reserve suffixing Type when dealing with the .NET type Type.

当使用泛型时,使用大写字母作为类型。不要加 Type 后缀。

// 正确:public class LinkedList<K,T>

{…}

// 错误:public class LinkedList<KeyType,DataType>

{…}

12. Use meaningful namespaces such as the product name or the company name.

使用有意义的命名空间,比如产品名或公司名。13. Avoid fully qualified type names. Use the using statement instead.

不要直接使用带命名空间的完整类型名称。使用 using 语句引用。14. Avoid putting a using statement inside a namespace.

不要将 using 语句放到命名空间里面。15. Group all framework namespaces together and put custom or third-party namespaces

underneath.

将引用的 framework 的命名空间组合排放,自定义的和第三方的命名空间放到最下面。

Page 8: IDesign CSharp 编码规范 - 中文译版

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using MyCompany;

using MyControls;

16. Use delegate inference instead of explicit delegate instantiation.

使用委托定义而不是直接的委托实例。

delegate void SomeDelegate();

public void SomeMethod()

{…}

private SomeDelegate someDelegate = SomeMethod;

17. Maintain strict indentation. Do not use tabs or nonstandard indentation, such as one space. Recommended values are three or four spaces, and the value should be uniform across.

严格使用缩进。不要使用 tabs 或非标准缩进,如单个空格。推荐使用 3 个或 4 个空格,这个值应该统一。

18. Indent comments at the same level of indentation as the code you are documenting.

注释的缩进应该和被注释的代码使用同样的缩进。19. All comments should pass spell checking. Misspelled comments indicate sloppy development.

所有的注释应该通过拼写检查。错误的拼写意味着垃圾的开发。20. All member variables should be declared at the top, with one line separating them from the

properties or methods.

所有的成员变量应该在顶部定义,与属性和方法使用一个空行隔开。

public class MyClass

{

private int m_Number;

private string m_Name;

public void SomeMethod1()

{}

public void SomeMethod2()

{}

}

21. Declare a local variable as close as possible to its first use.

局部变量的定义尽量靠近第一次被使用的地方。22. A file name should reflect the class it contains.

文件名应该体现它包含的类。

Page 9: IDesign CSharp 编码规范 - 中文译版

23. When using partial types and allocating a part per file, name each file after the logical part that part plays.

当使用 partial 类时,包含每个部分的文件名应包含这个部分的逻辑含义。

// MyClass.cs 文件:public partial class MyClass

{…}

// MyClass.Designer.cs 文件:public partial class MyClass

{…}

24. Always place an open curly brace ({) in a new line.

总是将大括号 ({) 放到新的一行里。25. With anonymous methods, mimic the code layout of a regular method, aligned with the

delegate declaration. (Complies with placing an open curly brace in a new line):

使用匿名方法时,代码风格应该和普通方法一样,与委托声明对齐。(大括号应该放到新的一行里):

delegate void SomeDelegate(string someString);

// 正确:void InvokeMethod()

{

SomeDelegate someDelegate = delegate(string name)

{

MessageBox.Show(name);

};

someDelegate("Juval");

}

// 错误:void InvokeMethod()

{

SomeDelegate someDelegate = delegate(string name){MessageBox.Show(name);};

someDelegate("Juval");

}

26. Use empty parentheses on parameter-less anonymous methods. Omit the parentheses only if the anonymous method could have been used on any delegate:

没有参数的匿名方法加上空的圆括号。除非这个匿名方法可能被用到任何类型的委托上。

Page 10: IDesign CSharp 编码规范 - 中文译版

delegate void SomeDelegate();

// 正确:SomeDelegate someDelegate1 = delegate()

{

MessageBox.Show("Hello");

};

// 错误:SomeDelegate someDelegate1 = delegate

{

MessageBox.Show("Hello");

};

27. With Lambda expressions, mimic the code layout of a regular method, aligned with the delegate declaration. Omit the variable type and rely on type inference, yet use parentheses:

使用 Lambda 表达式时,代码风格应该和普通方法一样,与委托声明对齐。省略变量类型,使用圆括号:

delegate void SomeDelegate(string someString);

SomeDelegate someDelegate = (name) =>

{

Trace.WriteLine(name);

MessageBox.Show(name);

};

28. Only use in-line Lambda expressions when they contain a single simple statement. Avoid multiple statements that require a curly brace or a return statement with inline expressions. Omit parentheses:

仅当 Lambda 表达式只包含一行简单语句的时候使用内嵌的风格。当包含多个语句和 return 语句的时候,不要使用内嵌的风格。省略圆括号:

delegate void SomeDelegate(string someString);

void MyMethod(SomeDelegate someDelegate)

{}

// 正确:MyMethod(name => MessageBox.Show(name));

// 错误:MyMethod((name)=> { Trace.WriteLine(name);MessageBox.Show(name);});

Page 11: IDesign CSharp 编码规范 - 中文译版

2. Coding Practices 编码实践

1. Avoid putting multiple classes in a single file.

不要将多个类放到一个文件中。2. A single file should contribute types to only a single namespace. Avoid having multiple

namespaces in the same file.

一个文件中应该只包含一个命名空间。3. Avoid files with more than 500 lines (excluding machine-generated code).

避免文件大小超过 500行,除非是机器生成的代码。4. Avoid methods with more than 200 lines.

避免方法超过 200行。5. Avoid methods with more than 5 arguments. Use structures for passing multiple arguments.

避免方法有超过 5 个参数,否者使用结构来传递多个参数。6. Lines should not exceed 120 characters.

每行应该不超过 120 个字符。7. Do not manually edit any machine-generated code.

不要手工修改机器生成的代码。a. If modifying machine generated code, modify the format and style to match this coding

standard.

如果修改机器生成的代码,那代码风格应该遵从代码规范。b. Use partial classes whenever possible to factor out the maintained portions.

尽量使用 partial 类来维护修改的代码。8. Avoid comments that explain the obvious. Code should be self-explanatory. Good code with

readable variable and Method names should not require comments.

不要对显而易见的逻辑增加注释。代码应该是自解释的。好的代码使用好的变量名和方法名而不需要注释。

9. Document only operational assumptions, algorithm insights and so on.

仅对算法、假设等进行文档化。10. Avoid method-level documentation.

避免方法级的文档。a. Use extensive external documentation for API documentation.

对 API 文档使用外部文件。b. Use method-level comments only as tool tips for other developers.

方法级上的注释只用作给其他开发人员的提示。

Page 12: IDesign CSharp 编码规范 - 中文译版

11. With the exception of zero and one, never hard-code a numeric value; always declare a constant instead.

除了 0 和 1外,不要在代码中使用任何数字,声明和使用常量。12. Use the const directive only on natural constants such as the number of days of the week.

只对自然存在的数字,比如一个星期的天数等使用 const 关键字。13. Avoid using const on read-only variables. For that, use the readonly directive.

只读的变量不要定义为 const,使用 readonly 关键字。

public class MyClass

{

public const int DaysInWeek = 7;

public readonly int Number;

public MyClass(int someValue)

{

Number = someValue;

}

}

14. Assert every assumption. On average, every fifth line is an assertion.

对所有假设使用断言。一般来说,每 15行代码应该有一个断言。

using System.Diagnostics;

object GetObject()

{…}

object someObject = GetObject();

Debug.Assert(someObject != null);

15. Every line of code should be walked through in a “white box” testing manner.

每一行代码都应该被白盒测试覆盖。16. Catch only exceptions for which you have explicit handling.

只捕捉要显式处理的异常。17. In a catch statement that throws an exception, always throw the original exception (or another

exception constructed from the original exception) to maintain the stack location of the original error:

在 catch 语句中抛出异常时,总是抛出原始异常(或从原始异常中构造出的其他异常)来维护错误堆栈中的位置:

Page 13: IDesign CSharp 编码规范 - 中文译版

catch(Exception exception)

{

MessageBox.Show(exception.Message);

throw;

}

18. Avoid error codes as method return values.

避免使用错误代码作为方法的返回值。19. Avoid defining custom exception classes.

避免使用自定义的异常类。20. When defining custom exceptions:

当自定义异常时:a. Derive the custom exception from Exception.

自定义的异常类应该继承 Exception 类。b. Provide custom serialization.

提供自定义的序列化。21. Avoid multiple Main() methods in a single assembly.

在一个项目中避免出现多个 Main() 方法。22. Make only the most necessary types public, mark others as internal.

只公开必要的类型,对其他类型标记为 internal 。23. Avoid friend assemblies, as they increase inter-assembly coupling.

避免 friend程序集,这样会增加程序集引用。24. Avoid code that relies on an assembly running from a particular location.

避免代码依赖于特定位置运行的程序集。25. Minimize code in application assemblies (EXE client assemblies). Use class libraries instead to

contain business logic.

最小化可执行程序集(EXE 文件程序集)的代码。把逻辑都放到单独的类库中。26. Avoid providing explicit values for enums unless they are integer powers of 2:

避免显式指定枚举值的数值,除非它们是 2 的指数:

Page 14: IDesign CSharp 编码规范 - 中文译版

// 正确:public enum Color

{

Red,Green,Blue

}

// 错误:public enum Color

{

Red = 1,

Green = 2,

Blue = 3

}

27. Avoid specifying a type for an enum.

避免使用指定类型的枚举类型。

// 错误:public enum Color : long

{

Red,Green,Blue

}

28. Always use a curly brace scope in an if statement, even if it conditions a single statement.

就算 if 块中只有一个语句也使用大括号。29. Avoid using the ternary conditional operator.

避免使用 3元条件运算符。30. Avoid explicit code exclusion of method calls (#if…#endif). Use conditional methods instead:

避免显式使用编译器条件指令(#if…#endif)。使用带条件标记的方法:

[Conditional("MySpecialCondition")]

public void MyMethod()

{ }

31. Avoid function calls in Boolean conditional statements. Assign into local variables and check on them.

避免在条件中直接调用返回布尔值的函数。把返回值赋值给一个局部变量来使用。

Page 15: IDesign CSharp 编码规范 - 中文译版

bool IsEverythingOK()

{}

// 错误:if(IsEverythingOK())

{…}

// 正确:bool ok = IsEverythingOK();

if(ok)

{…}

32. Always use zero-based arrays.

使用 0 为开始索引的数据。33. With indexed collection, use zero-based indexes

有索引的集合使用 0 开始的索引。34. Always explicitly initialize an array of reference types using a for loop.

总是用 for 循环显式初始化引用类型的数组。

public class MyClass

{}

const int ArraySize = 100;

MyClass[] array = new MyClass[ArraySize];

for(int index = 0;index<array.Length;index++)

{

array[index] = new MyClass();

}

35. Do not provide public or protected member variables. Use properties instead.

不要使用公开的或保护的类成员变量,使用属性。36. Avoid explicit properties that do nothing except access a member variable. Use automatic

properties instead:

尽量使用自动属性。

Page 16: IDesign CSharp 编码规范 - 中文译版

//错误:class MyClass

{

int m_Number;

public int Number

{

get

{

return m_Number;

}

set

{

m_Number = value;

}

}

}

//正确:class MyClass

{

public int Number

{

get;set;

}

}

37. Avoid using the new inheritance qualifier. Use override instead.

避免使用 new 继承关键字,使用 override 。38. Always mark public and protected methods as virtual in a non-sealed class.

对于非密封类,公开和保护的方法总是标记为 virtual 。39. Never use unsafe code, except when using interop.

除了 interop 外,决不使用使用 unsafe 代码。40. Avoid explicit casting. Use the as operator to defensively cast to a type.

避免使用显式类型转换。使用 as 操作符做类型转换。

Dog dog = new GermanShepherd();

GermanShepherd shepherd = dog as GermanShepherd;

if(shepherd != null)

{…}

41. Always check a delegate for null before invoking it.

调用委托前总是判断它是否为空。42. Do not provide public event member variables. Use event accessors instead.

不要公开事件成员变量。使用时间访问器来公开。

Page 17: IDesign CSharp 编码规范 - 中文译版

public class MyPublisher

{

MyDelegate m_SomeEvent;

public event MyDelegate SomeEvent

{

add

{

m_SomeEvent += value;

}

remove

{

m_SomeEvent -= value;

}

}

}

43. Avoid defining event-handling delegates. Use EventHandler<T> or GenericEventHandler instead. GenericEventHandler is defined in Chapter 6 of Programming .NET Components 2nd Edition.

避免定义 event-handling 委托。使用 EventHandler<T> 或者 GenericEventHandler

。GenericEventHandler 的定义请参考 《Programming .NET Components 2nd Edition》 第 6 章。44. Avoid raising events explicitly. Use EventsHelper to publish events defensively. EventsHelper is

presented in Chapter 6-8 of Programming .NET Components 2nd Edition.

避免显式引发事件。使用 EventsHelper 来引发事件。EventsHelper 的定义请参考《Programming .NET Components 2nd Edition》 第 6-8 章。

45. Always use interfaces. See Chapters 1 and 3 in Programming .NET Components 2nd Edition.

尽量使用接口。参见《Programming .NET Components 2nd Edition》 第 1-3 章。46. Classes and interfaces should have at least 2:1 ratio of methods to properties.

类和接口中方法和属性的比例应至少为 2:1 。47. Avoid interfaces with one member.

避免空接口。48. Strive to have three to five members per interface.

力求每个接口有 3 到 5 个成员。49. Do not have more than 20 members per interface. Twelve is probably the practical limit.

接口不应有超过 20 个成员。一般来说不会超过 12 个。50. Avoid events as interface members.

避免在接口中声明事件。51. When using abstract classes, offer an interface as well.

定义抽象类时同时定义一个接口。52. Expose interfaces on class hierarchies.

暴露接口的类层次结构

Page 18: IDesign CSharp 编码规范 - 中文译版

53. Prefer using explicit interface implementation.

推荐使用显示的接口实现。54. Never assume a type supports an interface. Defensively query for that interface.

不要假设一个类型实现了一个接口。

SomeType obj1;

IMyInterface obj2;

/* 初始化 obj1: */

obj2 = obj1 as IMyInterface;

if (obj2 != null)

{

obj2.Method1();

}

else

{

//处理错误}

55. Never hardcode strings that will be presented to end users. Use resources instead.

不要硬编码要显示给用户的字符串。使用资源。56. Never hardcode strings that might change based on deployment such as connection strings.

不要硬编码部署相关的字符串比如数据库连接字符串。57. Use String.Empty instead of "":

不要使用 “” ,使用 String.Empty 。

// 错误:string name = "";

//正确:string name = String.Empty;

58. When building a long string, use StringBuilder, not string.

处理长字符串时,使用 StringBuilder 而不是直接使用 String 。59. Avoid providing methods on structures.

避免在结构里包含方法。a. Parameterized constructors are encouraged.

尽量使用带参数的构造方法。b. Can overload operators.

可以重载操作符。60. Always provide a static constructor when providing static member variables.

当有静态成员时,总是定义一个静态构造函数。61. Do not use late-binding invocation when early-binding is possible.

当可以使用前期绑定时不要使用后期绑定。

Page 19: IDesign CSharp 编码规范 - 中文译版

62. Use application logging and tracing.

使用日志和跟踪。63. Never use goto unless in a switch statement fall-through.

除了在 switch 块中,不要使用 goto 语句。64. Always have a default case in a switch statement that asserts.

在 switch 中总是加入 default 分支。

int number = SomeMethod();

switch (number)

{

case 1:

Trace.WriteLine("Case 1:");

break;

case 2:

Trace.WriteLine("Case 2:");

break;

default:

Debug.Assert(false);

break;

}

65. Do not use the this reference unless invoking another constructor from within a constructor.

除非是调用另一个构造方法,不要使用 this 限定符。

// 使用 this 限定符的例子public class MyClass

{

public MyClass(string message)

{ }

public MyClass() : this("Hello")

{ }

}

66. Do not use the base word to access base class members unless you wish to resolve a conflict with a subclasses member of the same name or when invoking a base class constructor.

不要使用 base 限定符去访问基类成员,除非子类中有重写了这个成员或调用基类的构造函数。

Page 20: IDesign CSharp 编码规范 - 中文译版

// 使用 base 限定符的例子public class Dog

{

public Dog(string name)

{ }

virtual public void Bark(int howLong)

{ }

}

public class GermanShepherd : Dog

{

public GermanShepherd(string name) : base(name)

{ }

override public void Bark(int howLong)

{

base.Bark(howLong);

}

}

67. Do not use GC.AddMemoryPressure().

不要使用 GC.AddMemoryPressure() 。68. Do not rely on HandleCollector.

不要依赖于 HandleCollector 。69. Implement Dispose() and Finalize() methods based on the template in Chapter 4 of

Programming .NET Components 2nd Edition.

实现 Dispose() 和 Finalize() 时,参考《Programming .NET Components 2nd Edition》 第 4 章的模板。

70. Always run code unchecked by default (for the sake of performance), but explicitly in checked mode for overflow- or underflow-prone operations:

总是在 unchecked 模式下运行代码,对需要检查溢出的地方显示声明 checked 。

Page 21: IDesign CSharp 编码规范 - 中文译版

int CalcPower(int number, int power)

{

int result = 1;

for (int count = 1; count <= power; count++)

{

checked

{

result *= number;

}

}

return result;

}

71. Avoid casting to and from System.Object in code that uses generics. Use constraints or the as operator instead:

当使用泛型时,避免将其他类型显示转换成 System.Object 从显示转换回其他类型。使用约束或 as 操作符:

class SomeClass

{ }

//错误:class MyClass<T>

{

void SomeMethod(T t)

{

object temp = t;

SomeClass obj = (SomeClass)temp;

}

}

//正确:class MyClass<T> where T : SomeClass

{

void SomeMethod(T t)

{

SomeClass obj = t;

}

}

72. Do not define constraints in generic interfaces. Interface-level constraints can often be replaced by strong-typing.

不要再使用泛型的接口上增加约束。接口上的约束应该用强类型来实现。

Page 22: IDesign CSharp 编码规范 - 中文译版

public class Customer

{…}

// 错误:public interface IList<T> where T : Customer

{…}

// 正确:public interface ICustomerList : IList<Customer>

{…}

73. Do not define method-specific constraints in interfaces.

接口里不要定义方法级的约束。74. Do not define constraints in delegates.

委托上不要定义约束。75. If a class or a method offers both generic and non generic flavors, always prefer using the

generics flavor.

如果一个类或方法有泛型和非泛型 2 中实现,优先使用带泛型的实现。76. When implementing a generic interface that derives from an equivalent non-generic interface

(such as IEnumerable<T>), use explicit interface implementation on all methods, and implement the non-generic methods by delegating to the generic ones:

当实现一个从非泛型接口上集成下来的带泛型的接口时(比如 IEnumerable<T>),在所有的方法上使用显示实现,对非泛型的方法的实现通过调用泛型实现来完成:

class MyCollection<T> : IEnumerable<T>

{

IEnumerator<T> IEnumerable<T>.GetEnumerator()

{}

IEnumerator IEnumerable.GetEnumerator()

{

IEnumerable<T> enumerable = this;

return enumerable.GetEnumerator();

}

}

Page 23: IDesign CSharp 编码规范 - 中文译版

3. Project Settings and Project Structure 项目设置和项目结构

1. For Target Framework, always select the earliest target framework required for your solution, unlike the default which will give you the latest:

对目标框架版本,总是使用项目里用到的尽量早的版本,而不是缺省版本。

2. Always build your project with warning level 4

总是使用警告等级 4 来编译项目。

Page 24: IDesign CSharp 编码规范 - 中文译版

3. Treat warnings as errors in the Release build (note that this is not the default of Visual Studio). Although it is optional, this standard recommends treating warnings as errors in Debug builds as well.

在编译发布版本时,把警告做为错误抛出(非 Visual Studio 缺省设置)。在调试版本中也推荐这样做。

Page 25: IDesign CSharp 编码规范 - 中文译版

4. Avoid suppressing specific compiler warnings.

避免忽略编译器警告。5. Always explicitly state your supported runtime versions in the application configuration file.

总是在配置文件中显示声明需要的 framework 版本。

<?xml version="1.0"?>

<configuration>

<startup>

<supportedRuntime version="v2.0.50727.0"/>

<supportedRuntime version="v1.1.4322.0"/>

</startup>

</configuration>

6. Avoid explicit custom version redirection and binding to CLR assemblies.

避免显示的自定义版本重定向和绑定到 CLR 程序集。

Page 26: IDesign CSharp 编码规范 - 中文译版

7. Avoid explicit preprocessor definitions (#define). Use the project settings for defining conditional compilation constants.

避免显示的预编译指令(#define)。使用项目设置中的条件编译选项。8. Do not put any logic inside AssemblyInfo.cs.

不要再 AssemblyInfo.cs 中包含任何逻辑。9. Do not put any assembly attributes in any file besides AssemblyInfo.cs.

不要将程序集属性放到 AssemblyInfo.cs 外的任何文件中。10. Populate all fields in AssemblyInfo.cs such as company name, description, and copyright notice.

填充 AssemblyInfo.cs 中所有属性的值,如公司名、描述和版权信息等。11. All assembly references in the same solution should use relative paths.

一个解决方案中所有的程序集引用应该都使用相对路径。12. Disallow cyclic references between assemblies.

不允许在程序集中使用循环引用。13. Avoid multi-module assemblies.

避免程序集包含多个模块。14. Avoid tampering with exception handling using the Exception window (Debug|Exceptions).

不要随意改动调试->异常处理中的设置。15. Strive to use uniform version numbers on all assemblies and clients in the same logical

application (typically a solution). Use the SolutionInfo.cs technique from Chapter 5 of Programming .NET Components 2nd Edition to automate.

在同一个应用程序(通常是一个解决方案)中使用相同的版本号信息。使用《Programming .NET Components 2nd Edition》 第 5 章中描述的方法来自动完成。

Page 27: IDesign CSharp 编码规范 - 中文译版

16. Link all solution-wide information to a global shared SolutionInfo.cs file.

把所有解决方案中共享的信息放到一个全局共享的 SolutionInfo.cs 文件中。

Page 28: IDesign CSharp 编码规范 - 中文译版

17. Name your application configuration file as App.config, and include it in the project.

把程序个配置文件放在项目中,命名为 App.config 。18. Modify Visual Studio 2008 default project structure to comply with your project standard layout,

and apply uniform structure for project folders and files.

修改 Visual Studio 2008 中缺省的项目结构进行修改,对目录和文件都使用项目的标准结构。19. A Release build should contain debug symbols.

发布版本的编译应该包含调试符号。

20. Always sign your assemblies, including the client applications.

总是对程序集进行强签名。21. Use password-protected keys.

使用密码保护的键值。

Page 29: IDesign CSharp 编码规范 - 中文译版

4. Framework Specific Guidelines 框架代码准则

4.1. Data Access 数据访问1. Always use type-safe data sets or data tables. Avoid raw ADO.NET.

总是使用强类型的 DataSet 和 DataTable 。2. Always use transactions when accessing a database.

访问数据库时总是使用事务。a. Always use WCF, Enterprise Services or System.Transactions transactions.

总是使用 WCF 、Enterprise Services 或 System.Transactions 事务。b. Do not use ADO.NET transactions by enlisting the database explicitly.

不要显示使用 ADO.NET 的数据库级事务。3. Always use transaction isolation level set to Serializable. Management decision is required to

use anything else.

总是使用事务隔离级别来序列化。4. Do not use the Data Source window to drop connections on windows forms, ASP.NET forms or

web services. Doing so couples the presentation tier to the data tier.

不要在窗体、页面或 web 服务中使用数据源窗口中拖出来的连接。这样会在表现层中混入数据访问逻辑。

5. Avoid SQL Server authentication. Use Windows authentication instead.

访问数据库时避免使用 Sql Server 认证,使用 Windows 认证。6. Run components accessing SQL Server under separate identity from that of the calling client.

使访问 SQL Server 的部件在一个单独的帐号里运行。7. Always wrap your stored procedures in a high level, type safe class. Only that class invokes the

stored procedures. Let Visual Studio 2008 type-safe data adapters automate as much of that as possible.

将储存过程封装到个一个强类型的类中。仅使用这个类访问存储过程。如果可能的话,使用 Visual Studio 2008 强类型的 data adapter 来实现。

8. Avoid putting any logic inside a stored procedure. If you have anything more complex than simple switching logic to vary your query based on the parameter values, you should consider putting that logic in the business logic of the consuming code.

避免在存储过程中加入逻辑。如果有的话,使用存储过程的参数来控制逻辑,尽量在业务逻辑层来实现。

4.2. ASP.NET and Web Services1. Avoid putting code in ASPX files of ASP.NET. All code should be in the code beside partial class.

ASPX 页面中不应该有代码。所有代码应该在 code behind 的类文件中。

Page 30: IDesign CSharp 编码规范 - 中文译版

2. Code in code beside partial class of ASP.NET should call other components rather than contain direct business logic.

ASPX 页面的类文件中应该调用业务逻辑层的部件而不是直接包含业务逻辑的代码。3. Always check a session variable for null before accessing it.

访问 session 中的变量前,总是检查是否为空。4. In transactional pages or web services, always store session in SQL server.

使用包含事务的页面或web services 时,将 session 保存在数据库服务器中。5. Avoid setting the Auto-Postback property of server controls in ASP.NET to True.

避免将自动 Postback 属性设为有效。6. Turn on Smart Navigation for ASP.NET pages.

在 ASP.NET 页面中开启智能导航。7. Strive to provide interfaces for web services. See Appendix A of Programming .NET Components

2nd Edition.

尽量为 web services提供接口。参见《Programming .NET Components 2nd Edition》 附录 A。8. Always provide a namespace and service description for web services.

总是为 web services 提供命名空间和描述信息。9. Always provide a description for web methods.

总是为 web methods 提供描述信息。10. When adding a web service reference, provide a meaningful name for the location.

添加 Web Services 引用时,对引用路径使用有意义的名称。11. In both ASP.NET pages and web services, wrap a session variable in a local property. Only that

property is allowed to access the session variable, and the rest of the code uses the property, not the session variable.

在页面和 Web Services 中,对 session 的访问总是用一个私有属性包装起来,其他代码都通过这个属性来访问。

Page 31: IDesign CSharp 编码规范 - 中文译版

public class Calculator : WebService

{

int Memory

{

get

{

int memory = 0;

object state = Session["Memory"];

if (state != null)

{

memory = (int)state;

}

return memory;

}

set

{

Session["Memory"] = value;

}

}

[WebMethod(EnableSession = true)]

public void MemoryReset()

{

Memory = 0;

}

}

12. Always modify a client-side web service wrapper class to support cookies, since you have no way of knowing whether the service uses Session state or not.

总是修改客户端的 Web Services引用包装器以支持 cookie,因为你永远不会知道这个服务是否支持 session state。

public class Calculator : SoapHttpClientProtocol

{

public Calculator()

{

CookieContainer = new System.Net.CookieContainer();

Url = …;

}

}

4.3. Multithreading 多线程1. Use Synchronization Domains. See Chapter 8 in Programming .NET Components 2nd Edition.

Avoid manual synchronization because that often leads to deadlocks and race conditions.

Page 32: IDesign CSharp 编码规范 - 中文译版

使用同步的 Domains。参见《Programming .NET Components 2nd Edition》 第 8 章。避免手工同步,因为这很容易导致死锁。

2. Never call outside your synchronization domain.3. Manage asynchronous call completion on a callback method. Do not wait, poll, or block for

completion.

使用 callback 函数来结束异步调用。不要等待、拉或者阻塞。4. Always name your threads. The name is traced in the debugger Threads window, making debug

sessions more productive.

总是对线程进行命名。它会显示在调试窗口中,这样调试会更容易些。

Thread currentThread = Thread.CurrentThread;

string threadName = "Main UI Thread";

currentThread.Name = threadName;

5. Do not call Suspend() or Resume() on a thread.

不要调用线程的 Suspend() 和 Resume() 方法。6. Do not call Thread.Sleep(), except in the following conditions:

不要调用 Thread.Sleep(),除非发生以下情况:a. Thread.Sleep(0) is an acceptable optimization technique to force a context switch.

为了优化上下文切换而调用 Thread.Sleep(0) 。b. Thread.Sleep() is acceptable in testing or simulation code.

在测试代码中。7. Do not call Thread.SpinWait().

不要调用 Thread.SpinWait() 。8. Do not call Thread.Abort() to terminate threads. Use a synchronization object instead to signal

the thread to terminate. See Chapter 8 in Programming .NET Components 2nd Edition.

不要调用 Tread.Abort() 来终止线程。使用信号来终止。参见《Programming .NET

Components 2nd Edition》 第 8 章。9. Avoid explicitly setting thread priority to control execution. You can set thread priority based on

task semantic, such as below normal (ThreadPriority.BelowNormal) for a screen saver.

不要显式的设置线程优先级来控制运行。设置线程优先级只是基于线程的任务,比如对屏幕保护使用(ThreadPriority.BelowNormal)优先级。

10. Do not read the value of the ThreadState property. Use Thread.IsAlive to determine whether the thread is dead or alive.

不要读 ThreadState 属性的值。使用 Thread.IsAlive 来检测线程是否活动。11. Do not rely on setting the thread type to background thread for application shutdown. Use a

watchdog or other monitoring entity to deterministically kill threads.

不要依赖于应用程序关闭时自动终止后台线程。使用监视器手工终止线程。12. Do not use thread local storage unless thread affinity is guaranteed.

Page 33: IDesign CSharp 编码规范 - 中文译版

13. Do not call Thread.MemoryBarrier().

不要调用 Thread.MemoryBarrier() 。14. Never call Thread.Join() without checking that you are not joining your own thread.

在调用 Thread.Join() 前一定要检查是否在 Join 线程自身。

void WaitForThreadToDie(Thread thread)

{

Debug.Assert(Thread.CurrentThread.ManagedThreadId != thread.ManagedThreadId);

thread.Join();

}

15. Always use the lock() statement rather than explicit Monitor manipulation.

使用 lock() 好过显示的 Monitor 操作。16. Always encapsulate the lock() statement inside the object it protects.

总是将 lock() 语句放在它要保护的对象里面。

public class MyClass

{

public void DoSomething()

{

lock (this)

{…}

}

}

17. You can use synchronized methods instead of writing the lock() statement yourself.

尽量使用同步调用,而不是是 lock() 语句。18. Avoid fragmented locking (see Chapter 8 of Programming .NET Components 2nd Edition).19. Avoid using a Monitor to wait or pulse objects. Use manual or auto-reset events instead.

不要使用 Monitor 来等待或 pulse 对象。使用手工或 auto-reset event 。20. Do not use volatile variables. Lock your object or fields instead to guarantee deterministic and

thread-safe access. Do not use Thread.VolatileRead(), Thread.VolatileWrite(), or the volatile modifier.

不要使用 volatile 变量。对对象或域加锁来保证线程安全。不要使用 Thread.VolatileRead()

、 Thread.VolatileWrite() 或 volatile 修饰符。21. Avoid increasing the maximum number of threads in the thread pool.

不要增加线程池中最大线程数。22. Never stack lock statements because that does not provide atomic locking. Use

WaitHandle.WaitAll() instead.

不要级联使用 lock 语句。使用 WaitHandle.WaitAll() 。

Page 34: IDesign CSharp 编码规范 - 中文译版

MyClass obj1 = new MyClass();

MyClass obj2 = new MyClass();

MyClass obj3 = new MyClass();

// 不要级联使用 lock

lock (obj1)

lock (obj2)

lock (obj3)

{

obj1.DoSomething();

obj2.DoSomething();

obj3.DoSomething();

}

4.4. Serialization 序列化1. Prefer the binary formatter.

优先考虑二进制序列化。2. Mark serialization event handling methods as private.

处理序列化的时间标记为私有。3. Use the generic IGenericFormatter interface. See Chapter 9 of Programming .NET Components

2nd Edition.

使用泛型的 IGenericFormatter 接口。参见《Programming .NET Components 2nd Edition》 第9 章。

4. Mark non-sealed classes as serializable.

将未密封的类标记为可序列化。5. When implementing IDeserializationCallback on a non-sealed class, make sure to do so in a way

that allowed subclasses to call the base class implementation of OnDeserialization().See Chapter 3 of Programming .NET Components 2nd Edition.

当一个未密封类实现 IDeserializationCallback接口时,确保子类可以调用基类的OnDeserialization()方法。参见《Programming .NET Components 2nd Edition》 第 3 章。

6. Always mark un-serializable member variables as non serializable.

总是将不可以序列化的成员标记为不可序列化。7. Always mark delegates on a serialized class as non-serializable fields:

总是将可序列化的类上的委托成员标记为不可序列化。

Page 35: IDesign CSharp 编码规范 - 中文译版

[Serializable]

public class MyClass

{

[field: NonSerialized]

public event EventHandler MyEvent;

}

4.5. Remoting1. Prefer administrative configuration to programmatic configuration.2. Always implement IDisposable on single call objects.3. Always prefer a TCP channel and a binary format when using remoting, unless a firewall is present.4. Always provide a null lease for a singleton object.

public class MySingleton : MarshalByRefObject

{

public override object InitializeLifetimeService()

{

return null;

}

}

5. Always provide a sponsor for a client activated object. The sponsor should return the initial lease time.

6. Always unregister the sponsor on client application shutdown.7. Always put remote objects in class libraries.8. Avoid using SoapSuds.9. Avoid hosting in IIS.10. Avoid using uni-directional channels.11. Always load a remoting configuration file in Main() even if the file is empty, and the application

does not use remoting.

static void Main()

{

RemotingConfiguration.Configure("MyApp.exe.config");

/* Rest of Main() */

}

12. Avoid using Activator.GetObject() and Activator.CreateInstance() for remote objects activation. Use new instead.

13. Always register port 0 on the client side, to allow callbacks.14. Always elevate type filtering to full on both client and host to allow callbacks.

4.6. Security1. Always demand your own strong name on assemblies and components that are private to the

application, but are public (so that only you can use them).

Page 36: IDesign CSharp 编码规范 - 中文译版

public class PublicKeys

{

public const string MyCompany = "1234567894800000940000000602000000240000" +

"52534131000400000100010007D1FA57C4AED9F0" +

"A32E84AA0FAEFD0DE9E8FD6AEC8F87FB03766C83" +

"4C99921EB23BE79AD9D5DCC1DD9AD23613210290" +

"0B723CF980957FC4E177108FC607774F29E8320E" +

"92EA05ECE4E821C0A5EFE8F1645C4C0C93C1AB99" +

"285D622CAA652C1DFAD63D745D6F2DE5F17E5EAF" +

"0FC4963D261C8A12436518206DC093344D5AD293";

}

[StrongNameIdentityPermission(SecurityAction.LinkDemand,

PublicKey = PublicKeys.MyCompany)]

public class MyClass

{…}

2. Apply encryption and security protection on application configuration files.3. When importing an interop method, assert unmanaged code permission, and demand appropriate

permission instead.

[DllImport("user32", EntryPoint = "MessageBoxA")]

private static extern int Show(IntPtr handle, string text, string caption, int msgType);

[SecurityPermission(SecurityAction.Assert, UnmanagedCode = true)]

[UIPermission(SecurityAction.Demand,

Window = UIPermissionWindow.SafeTopLevelWindows)]

public static void Show(string text, string caption)

{

Show(IntPtr.Zero, text, caption, 0);

}

4. Do not suppress unmanaged code access via the SuppressUnmanagedCodeSecurity attribute.5. Do not use the /unsafe switch of TlbImp.exe. Wrap the RCW in managed code so that you could

assert and demand permissions declaratively on the wrapper.6. On server machines, deploy a code access security policy that grants only Microsoft, ECMA, and self

(identified by a strong name) full trust. Code originating from anywhere else is implicitly granted nothing.

7. On client machines, deploy a security policy which grants client application only the permissions to execute, to call back the server and to potentially display user interface. When not using ClickOnce, client application should be identified by a strong name in the code groups.

8. To counter a luring attack, always refuse at the assembly level all permissions not required to perform the task at hand.

[assembly: UIPermission(SecurityAction.RequestRefuse,Window = UIPermissionWindow.AllWindows)]

9. Always set the principal policy in every Main() method to Windows

Page 37: IDesign CSharp 编码规范 - 中文译版

public class MyClass

{

static void Main()

{

AppDomain currentDomain = Thread.GetDomain();

currentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);

}

//other methods

}

10. Never assert a permission without demanding a different permission in its place. See Chapter 12 in Programming .NET Components 2nd Edition.

4.7. System.Transactions1. Always dispose of a TransactionScope object.2. Inside a transaction scope, do not put any code after the call to Complete().3. When setting the ambient transaction, always save the old ambient transaction and restore it when

you are done.4. In Release builds, never set the transaction timeout to zero (infinite timeout).5. When cloning a transaction, always use6. DependentCloneOption.BlockCommitUntilComplete.7. Create a new dependent clone for each worker thread. Never pass the same dependent clone to

multiple threads.8. Do not pass a transaction clone to the TransactionScope's constructor.9. Always catch and discard exceptions thrown by a transaction scope that is set to

TransactionScopeOption.Suppress.

4.8. Enterprise Services1. Do not catch exceptions in a transactional method. Use the AutoComplete attribute. See Chapter 4

in COM and .NET Component Services.2. Do not call SetComplete(), SetAbort(), and the like. Use the AutoComplete attribute.

[Transaction]

public class MyComponent : ServicedComponent

{

[AutoComplete]

public void MyMethod(long objectIdentifier)

{…}

}

3. Always override CanBePooled and return true (unless you have a good reason not to return to pool)

Page 38: IDesign CSharp 编码规范 - 中文译版

public class MyComponent : ServicedComponent

{

protected override bool CanBePooled()

{

return true;

}

}

4. Always call Dispose() explicitly on a pooled objects unless the component is configured to use JITA as well.

5. Never call Dispose() when the component uses JITA.6. Always set authorization level to application and component.7. Set authentication level to privacy on all applications.

[assembly: ApplicationActivation(ActivationOption.Server)]

[assembly: ApplicationAccessControl(

true, //Authorization

AccessChecksLevel = AccessChecksLevelOption.ApplicationComponent,

Authentication = AuthenticationOption.Privacy,

ImpersonationLevel = ImpersonationLevelOption.Identify)]

8. Set impersonation level on client assemblies to Identity.9. Always set ComponentAccessControl attribute on serviced components to true (the default is true)

[ComponentAccessControl]

public class MyComponent : ServicedComponent

{…}

10. Always add to the Marshaler role the Everyone user

[assembly: SecurityRole("Marshaler", SetEveryoneAccess = true)]

11. Apply SecureMethod attribute to all classes requiring authentication.

[SecureMethod]

public class MyComponent : ServicedComponent

{...}

Page 39: IDesign CSharp 编码规范 - 中文译版

5. Resources

5.1. Programming .N ET Components 2nd EditionBy Juval Lowy, O'Reilly 2005 ISBN: 0-596-10207-0

5.2. The IDesign Serviceware DownloadsIDesign serviceware download is a set of original techniques, tools utilities and even breakthroughs developed by the IDesign architects. The utilities are largely productivityenhancing tools, or they compensate for some oversight in the design of .NET or WCF. The demos are also used during our Master Classes to demystify technical points, as lab exercises or to answer questions. The classes' attendees find the demos useful not only in class but after it. The demos serve as a starting point for new projects and as a rich reference and samples source.

5.3. The Architect’s Master ClassThe Architect’s Master Class is a five days training, and is the ultimate resource for the professional architect. The class is conducted in the style of a Master Class, where a Master architect shares his experience and perspective, and interacts with the students. The class has three parts, on process, technology and SOA, and the IDesign method. While the class shows how to design modern systems, it sets the focus on the ‘why’ and the rationale behind particular design decisions, often shedding light on poorly understood aspects. You will see relevant design guidelines, best practices, and pitfalls, and the crucial process skill required of today’s architects. Don’t miss on this unique opportunity to learn and

Page 40: IDesign CSharp 编码规范 - 中文译版

improve your architecture skills with IDesign, and share our passion for architecture and software engineering.

More at www.idesign.net