设计警告
设计警告支持遵守 .NET Framework 设计指导原则。
本节内容
规则 |
说明 |
---|---|
调用泛型类型的静态成员时,必须指定该类型的类型参数。 当调用不支持推理的泛型实例成员时,必须指定该成员的类型参数。 在上述两种情况下,用于指定类型参数的语法不同,但很容易混淆。 |
|
一个类声明并实现 System.IDisposable 类型的实例字段,但该类不实现 IDisposable。 声明 IDisposable 字段的类间接拥有非托管资源,并且应该实现 IDisposable 接口。 |
|
System.Collections.Generic.List<(Of <(T>)>) 是针对性能(而非继承)设计的泛型集合。 因此,List 不包含任何虚拟成员。 应改为公开针对继承设计的泛型集合。 |
|
类型包含返回 void 的委托,该委托的签名包含两个参数(第一个参数是对象,第二个参数是可以分配给 EventArgs 的类型),而且包含程序集针对的是 .NET Framework 2.0。 |
|
推理是指由传递给泛型方法的参数的类型来确定该方法的类型参数,而不是显式指定类型参数。 若要启用推理,泛型方法的参数签名必须包含与该方法的类型参数属于相同类型的参数。 在这种情况下,不必指定类型参数。 对所有类型参数都使用推理时,调用泛型实例方法和非泛型实例方法的语法完全相同;这简化了泛型方法的可用性。 |
|
泛型类型包含的类型参数越多,越难以知道并记住每个类型参数各代表什么。 它通常有一个类型参数,如 List<T> 中,而在某些情况下有两个类型参数,如 Dictionary<TKey, TValue> 中。 但是,如果存在两个以上的类型参数,则大多数用户都会感到过于困难。 |
|
嵌套类型参数是一个类型参数,也是一个泛型类型。 若要调用签名包含嵌套类型参数的成员,用户必须实例化一个泛型类型,并将此类型传递到另一个泛型类型的构造函数。 所需的过程和语法很复杂,应当避免。 |
|
外部可见方法包含类型为 System.Object 的引用参数。 使用泛型方法使受约束的所有类型都可以传递给该方法,而比不先将类型强制转换为引用参数类型。 |
|
像其他值类型一样,未初始化枚举的默认值为零。 无标志特性的枚举应通过使用零值来定义成员,这样默认值即为该枚举的有效值。 如果应用了 FlagsAttribute 特性的枚举定义值为零成员,则该成员的名称应为“None”,以指示枚举中尚未设置值。 |
|
事件处理程序方法采用两个参数。 第一个参数属于 System.Object 类型,名为“sender”。 它是引发事件的对象。 第二个参数属于 System.EventArgs 类型,名为“e”。 这是与该事件关联的数据。 事件处理程序方法不应返回值;在 C# 编程语言中,这由返回类型 void 指示。 |
|
若要扩大集合的用途,应实现某个泛型集合接口。 然后,可以使用该集合来填充泛型集合类型。 |
|
在方法声明中将基类型指定为参数时,可以将派生自基类型的任何类型作为相应的参数传递给方法。 如果不需要派生参数类型提供的其他功能,则使用基类型将使方法可以得到更广泛的使用。 |
|
抽象类型的构造函数只能由派生类型调用。 由于公共构造函数用于创建类型的实例,但无法为抽象类型创建实例,因此具有公共构造函数的抽象类在设计上是错误的。 |
|
公共或受保护类型实现加或减运算符时没有实现相等运算符。 |
|
公共语言规范 (CLS) 定义了程序集在跨编程语言使用时必须符合的命名限制、数据类型和规则。 好的设计要求所有程序集用 CLSCompliantAttribute 显式指示 CLS 合规性。 如果程序集没有此特性,则该程序集即不合规。 |
|
.NET Framework 使用版本号唯一地标识程序集,并绑定到具有强名称的程序集中的类型。 版本号与版本和发行者策略一起使用。 默认情况下,仅使用用于生成应用程序的程序集版本运行应用程序。 |
|
ComVisibleAttribute 决定 COM 客户端如何访问托管代码。 合理的设计指出程序集将显式指示 COM 可见性。 可以设置整个程序集的 COM 可见性,然后重写各个类型和类型成员的 COM 可见性。 如果此特性不存在,则程序集的内容对 COM 客户端可见。 |
|
当定义自定义特性时,用 AttributeUsageAttribute 标记该特性,以指示源代码中可以应用自定义特性的位置。 特性的含义和预定用法将决定它在代码中的有效位置。 |
|
特性可以定义强制实参,在对目标应用该特性时必须指定这些实参。 这些实参也称为位置实参,因为它们将作为位置形参提供给特性构造函数。 对于每一个强制变量,特性还必须提供一个相应的只读属性,以便可以在执行时检索该变量的值。 特性还可以定义可选实参,可选实参也称为命名实参。 这些变量按名称提供给特性构造函数,并且必须具有相应的读/写属性。 |
|
请确保每个命名空间都有一个逻辑组织,并确保将类型放入稀疏填充的命名空间的理由是有效的。 |
|
通过引用(使用 out 或 ref)传递类型要求具有使用指针的经验,了解值类型和引用类型的不同之处,以及能处理具有多个返回值的方法。 另外,out 和 ref 参数之间的差异没有得到广泛了解。 |
|
索引器(即索引属性)应该使用一个索引。 多维索引器会大大降低库的可用性。 |
|
公共或受保护方法的名称以“Get”开头,没有采用任何参数或返回的值不是数组。 该方法可能很适于成为属性。 |
|
如果参数的具体数量未知且变量参数为相同类型或可作为相同类型传递,请使用参数数组代替重复参数。 |
|
CLS 中允许使用默认参数的方法;但是 CLS 允许编译器忽略为这些参数分配的值。 为了跨编程语言维护所需的行为,必须使用提供默认参数的方法重载来替换使用默认参数的方法。 |
|
枚举是一种值类型,它定义一组相关的已命名常数。 如果可以按照有意义的方式组合一个枚举的已命名常数,则对该枚举应用 FlagsAttribute。 |
|
枚举是一种值类型,它定义一组相关的已命名常数。 默认情况下,System.Int32 数据类型用于存储常量值。 尽管您可以更改此基础类型,然而对于大多数情况,既不需要,也不建议您这样做。 |
|
该规则检测名称通常用于事件的方法。 如果为响应明确定义的状态更改而调用一个方法,则应由事件处理程序调用该方法。 调用该方法的对象应引发事件而不是直接调用该方法。 |
|
不应捕捉一般异常。 捕捉更具体的异常,或者在执行 catch 块中的最后一条语句时重新引发一般异常。 |
|
如果不能提供完整的构造函数集,要正确处理异常将变得比较困难。 |
|
未密封的外部可见类型提供了显式实现公共接口的方法,但没有提供具有相同名称的其他外部可见方法。 |
|
嵌套类型是在另一个类型的范围中声明的类型。 嵌套类型用于封装包含类型的私有实现详细信息。 如果用于此用途,则嵌套类型不应是外部可见的。 |
|
此规则要求 ICollection 实现提供强类型成员,以使用户在使用该接口提供的功能时不必将参数强制转换成 Object 类型。 此规则假定实现 ICollection 的类型这样做是为了管理其类型强于对象的实例的集合。 |
|
公共或受保护类型实现 System.IComparable 接口。 它不重写 Object.Equals,也不重载表示相等、不等、小于或大于的语言特定运算符。 |
|
此规则要求 IEnumerator 实现还提供 Current 属性的强类型版本,以使用户在使用该接口提供的功能时不必将返回值强制转换为强类型。 |
|
此规则要求 IList 实现提供强类型成员,以使用户在使用该接口提供的功能时不必将参数强制转换成 System.Object 类型。 |
|
接口定义提供某个行为或使用协定的成员。 接口所描述的功能可以被任何类型采用,而不管该类型出现在继承层次结构中的哪个位置。 类型通过实现接口的成员来实现接口。 空接口无法定义任何成员;因此,它无法定义可以实现的协定。 |
|
用未指定其 ObsoleteAttribute.Message 属性的 System.ObsoleteAttribute 特性来标记类型或成员。 编译用 ObsoleteAttribute 标记的类型或成员时,将显示特性的 Message 属性,这将为用户提供有关已过时类型或成员的信息。 |
|
索引器(即索引属性)应将整型或字符串类型用于索引。 这些类型一般用于为数据结构编制索引,并且提高库的可用性。 应仅限于在设计时无法指定特定整型或字符串类型的情况下使用 Object 类型。 |
|
虽然可以接受且经常需要使用只读属性,但设计准则禁止使用只写属性。 这是因为允许用户设置值但又禁止该用户查看这个值不能提供任何安全性。 而且,如果没有读访问,将无法查看共享对象的状态,使其用处受到限制。 |
|
通过引用(使用 out 或 ref)传递类型要求具有使用指针的经验,了解值类型和引用类型的不同之处,以及能处理具有多个返回值的方法。 为一般用户进行设计的库架构师不应指望用户能熟练运用 out 或 ref 参数。 |
|
对于引用类型,相等运算符的默认实现几乎始终是正确的。 默认情况下,仅当两个引用指向同一对象时,它们才相等。 |
|
类型声明受保护的成员,使继承类型可以访问或重写该成员。 按照定义,不能继承密封类型,这表示不能调用密封类型上的受保护方法。 |
|
类型将方法声明为虚方法,使继承类型可以重写虚方法的实现。 按照定义,不能继承密封类型。 这使得虚方法对于密封类型没有意义。 |
|
分配非托管资源的类型应该实现 IDisposable,以使调用方可以根据需要释放这些资源,并缩短持有这些资源的对象的生存期。 |
|
应在命名空间内声明类型以避免名称冲突,并作为一种在对象层次结构中组织相关类型的方式。 |
|
字段的主要用途应是作为实现的详细信息。 字段应为 private 或 internal,并应通过使用属性公开这些字段。 |
|
公共或受保护类型仅包含静态成员,而且没有用 sealed(C#)或 NotInheritable (Visual Basic) 修饰符声明该类型。 应使用 sealed 修饰符标记不希望被继承的类型,以免将其用作基类型。 |
|
公共或嵌套公共类型只声明了静态成员,但具有公共或受保护的默认构造函数。 由于调用静态成员不需要类型的示例,因此没必要使用构造函数。 为安全起见,字符串重载应使用字符串参数调用统一资源标识符 (URI) 重载。 |
|
如果某方法采用 URI 的字符串表示形式,则应提供采用 URI 类的实例的相应重载,该重载以安全的方式提供这些服务。 |
|
此规则假定该方法返回 URI。 URI 的字符串表示形式容易导致分析和编码错误,并且可造成安全漏洞。 System.Uri 类以一种安全的方式提供这些服务。 |
|
此规则假定该属性表示 URI。 URI 的字符串表示形式容易导致分析和编码错误,并且可造成安全漏洞。 System.Uri 类以一种安全的方式提供这些服务。 |
|
某个类型声明的方法重载与 System.Uri 参数仅在字符串参数的放置方面有所不同。 采用字符串参数的重载不调用采用 URI 参数的重载。 |
|
外部可见的类型扩展某些基类型。 请使用某个备选项。 |
|
具体类型是指具有一个完整实现因此可以实例化的类型。 若要使成员可以得到广泛使用,请使用建议的接口来替换具体类型。 |
|
平台调用方法(例如,用 System.Runtime.InteropServices.DllImportAttribute 标记的方法,或者在 Visual Basic 中使用 Declare 关键字定义的方法)可以访问非托管代码。 这些方法应属于 NativeMethods、SafeNativeMethods 或 UnsafeNativeMethods 类。 |
|
如果派生方法的参数签名只是在类型方面有所不同,而且与基方法的参数签名中的对应类型相比,这些类型的派生方式更弱,则基类型中的方法由派生类型中的同名方法隐藏。 |
|
对于传递给外部可见方法的所有引用参数,都应检查其是否为 null。 |
|
所有的 IDisposable 类型都应当正确实现 Dispose 模式。 |
|
内部异常仅在其自己的内部范围内可见。 当异常超出内部范围后,只能使用基异常来捕获该异常。 如果内部异常是从 System.Exception、System.SystemException 或 System.ApplicationException 继承的,外部代码将没有足够的信息了解如何处理该异常。 |
|
不应引发异常的方法引发了异常。 |
|
强名称可避免客户端在不知情的情况下加载已被篡改的程序集。 除非极为有限的几种情况,否则不应部署没有强名称的程序集。 如果共享或发布未正确签名的程序集,则该程序集可能被篡改,公共语言运行时可能不会加载该程序集;而用户可能必须在他/她的计算机上禁用验证。 |