zoukankan      html  css  js  c++  java
  • The Elements of C# Style Programming

    1.类型

      1.1 使用内建C#数据类型别名

          所有C#基本类型均有别名。例如,int是System.Int32的别名。

      1.2 避免使用内联字面量

          避免这样的代码   

    if(size > 45)
    {
    ...
    }

          最好声明一个常量

    const int Limit = 45 ;
    ...
    if(size > Limit)
    {
      //...
    } 

    1.3 避免不必要的值类型装箱

          装箱(boxing)将值类型转换为引用类型的操作,例如

    object obj = 4 ;

          拆箱(unboxing)是从引用类型向值类型的转换,例如

    int i = (int) obj ;

          装箱和拆箱明显增加性能上的开销,所以尽可能避免重复的装箱/拆箱操作。例如,如果想保存一组整数,宁可用List也不要用基于对象的ArrayList。

    1.4 使用标准形式书写浮点字面量

          在浮点数小数点前后均应是数字。

    const double Foo = 0.042e2 ;

            应该写成

    const double Foo = 4.2e0 ;
    const double Foo = 4.2 ;

    1.5 为“值”语义使用结构

    1.6 考虑在结构中覆盖等同方法和操作符

          struct应遵循值语义。因此,应覆盖等同方法和操作符,并且实现值行为而不是从System.Object继承引用行为。

    1.7 使用“@”前缀转义整个字符串

          使用“@”前缀转义字符串中的所有字符较为方便和易读。

    string path = "c:\\My Folder\\Acme" ;
    变成
    string path = @"c:\My Folder\Acme" ;

    1.8 避免代价昂贵的隐藏字符串分配

          字符串拼接也需要做一次字符串分配,使用stringBuilder类并在拼接结束后转换为string会更为有效。

    StringBuilder buffer = new StringBuilder() ;
    string temp = "";
    while(...)
    {
           buffer.Append(temp);
    }
    string str = buffer.ToString();

    1.9 采用有效的空字符串检测方法

    if(str.Equals(""))

          上面的代码需要创建一个新的空字符串。

          .NET 2.0 引入了静态方法String.IsNullOrEmpty(),用一次高效测试就能检测孔引用和孔字符串。

    1.10 只在必要时使用可空值

            

    int? a = null ;

          可空值虽然可以在多种场合使得a正常工作,但是,会带来性能上影响。

    1.11 仅为支持机器生产代码使用部分类型

    2. 语句和表达式

       2.1 在复杂表达式中不要依赖操作符优先级

          使用括号定义和控制子表达式求值会让代码易于理解和维护。

    int j = 10 * 2 << 1 + 20 ;
    //添加括号
    j = (10 * (2 << 1)) + 20 ;

      2.2 不用true或false测试相等

    bool b = false ;
     
    if (b == false)
    {
    //...
    }
    
    直接使用布尔值
    
    bool b = false ;
    if(!b)
    {
    //...
    }

    2.3 用等价方法替换重复出现的非普通表达式

          在编写代码时,找出可以拆分为单个方法的重复表达式或操作。

    2.4 在三元条件判断中避免使用复杂语句

          三元形式是一般用于简单的情况,复杂情况下不要使用。

    2.5 使用Object.Equals()测试引用类型的对象等同

          某个类型的非静态方法Equals()和==操作符可能会被另外一个使用值等同的类型所覆盖。如果想引用等同,那么使用Object.Equals()方法显示地做检查。

    if( Object.Equals(string1,string2))
    {
    //...
    }

    3.流程控制

    3.1 避免在循环语句中使用break和continue语句

          尽量组合使用if...else 语句和控制表达式产生使用break和continue完成的功能。如果你发现,为了避免使用break和continue语句需要创建和求值一堆额外的     状态变量或复制表达式,那么选择忽略此规则。在负责性和性能上付出的代价会抵消风格上的考虑。如果一定要使用break和continue语句,就添加明显的注释来突出这些特殊的出口。

    3.2 在方法中避免使用多个return 语句

          在函数块最后一个语句之外的任何地方使用return语句都会被打破正常的执行流程。

    3.3 不要使用goto

    3.4 不要使用try ... throw .. catch来管理控制流程

          尽可能之用C#的异常机制出了异常情况,不要将其用作条件表达式的替代品。

    3.5 在for语句内部声明循环变量

          循环变量应该在for语句中声明。这样,可以将变量的作用域限于for语句之内。

    for (int count = 0; count < length; count++)
    {
    //仅在此块中可见计数
    }

    3.6 给所有switch语句的结尾添加default标记

          并解释默认情况如何或如何不能运行到。

    switch(color)
    {
          case Color.Red:
          //...
          break;
          case Color.Blue:
          //...
          break;
          default:
          //这可能无法获取,因为假定颜色只是红、蓝
          Debug.Assert(false,"Invalid color value");
    }

    4. 类

    4.1 定义小类和小方法

          较小的类和方法易于设计、编码、测试、编写文档、阅读、理解和使用。较小的类通常拥有更少的方法,能表达更简单的概念,所以其接口倾向于表现出更好的内聚性。

    4.2 从标准类型构造基础类

          在设计底层的基础类型或具体类型时,应该尽量减少对非标准类型的依赖。尽可能限制自己只使用.NET定义的类型。

    4.3 避免在用户可扩展的类层次结构中使用虚基类

    4.4 声明所有成员的访问级别

          许多程序员遵循以下顺序放置声明:public,然后是protected,跟着private。

    4.5 将类标记为sealed,防止不想要的派生

    4.6 避免使用internal声明

    4.7 避免使用new来隐藏派生类型的成员

    4.8 限制base关键字在子类构造函数和覆盖方法中的使用

    4.9 在覆盖Equals()方法时也覆盖Opeartor==和Opeartor!=

    public boolean operator == (Student lhs,
                                                  Student rhs)
    {
    return lhs == null? rhs == null : lhs.Equals(rhs);
    }
    
    public bool Equals(Student rhs)
    {
    return ((rhs != null) && (id_ ==  rhs.id_)) ;
    }

    4.10 在覆盖ToString()方法时,考虑覆盖隐式字符串转换操作符

    4.11 用相反功能的方法来实现方法

    public void Deposit(double amount)
    {
    balance_ += amount ;
    }
    
    public void Withdraw(double amount)
    {
    Deposit(-amount);
    }

    5. 生命周期

    5.1 初始化所有变量

    5.2 总是构造在有效状态的对象

    5.3 为增加的透明性和COM互操作性声明显式默认构造函数

    5.4 声明构造函数为保护的,禁止直接实例化

    5.5 总是在派生构造函数的初始化列表中列出全部基构造函数

          即便是默认构造函数也要如此。

    public class Base
    {
        protected int foo_ ;
        public Base(int foo)
        {
         foo_ =  foo ;
        }
    }
    
    public class A : Base
    {
     public A (int foo)
      {
       foo_ = foo ;
      }
    
    }
    最好写成
    public class A : Base
    {
       public B(int foo) : base(foo)
      {
      }
    }

    5.6 使用嵌套的构造函数消除冗余代码

    public class Account
    {
          private string name_;
          private double balance_;
          const double DefaultBalance = 0.0d;
          
          public Account(string name, double balance)
          {
          name_ = name;
          balance_ = balance;
          }
          public Account(string name)
          {
          name_ = name;
          balance_ = DefaultBalance;
          }
    }

          上面的构造函数有冗余,最好变成下面的形式。

    public class Account
    {
          private string name_;
          private double balance_;
          const double DefaultBalance = 0.0d;
          
          public Account(string name, double balance)
          {
          name_ = name;
          balance_ = balance;
          }
          public Account(string name)
          {
          this(name,DefaultBalance );  
          }
    }

    5.7 在引用外部资源的类中实现IDisposable

          如果类提供了Dispose()和Close()方法,应总是调用。

          如果你的类拥有了外部或非受管资源,则总应实现IDisposable接口和模式。

          为IDisposable提供Dispose()方法,调用你自己的终止代码。

    6. 字段和属性

    6.1 声明所有字段为私有访问级别,使用属性提供访问。

    6.2 只为简单、低成本、与顺序无关的访问使用属性

    7. 方法

    7.1 避免传递过多参数

    7.2 检验参数值有效性

          如果方法对参数的值做了些假设,在方法开始处验证这些参数。

    8.特性

    8.1使用System.ObsoleteAttribute弃用API

          如果类库的不同版本导致了接口的改动,使用System.ObsoleteAttribute弃用旧接口,而不是简单地移除或改动接口。

    8.2 考虑新类是否可被串行化

    8.3 使用System.FlagsAttribute指明位域

          用System.FlagsAttribute 标注枚举,向编译器指出这些可以无需警示按位做或运算。

    [Flags]
    enum Access
    {
          None = 0 ,
          Read = 1,
          Write = 2,
          Admin = 4,
    };
    //...
    Access userPermissions = Access.Read | Access.Write;

    9. 泛型

    9.1 泛型类型胜过未指定类型或强类型类

    10.枚举

    10.1 使用枚举而非布尔型来改善参数可读性

    10.2 使用枚举值而非整型常量

    10.3 创建0值枚举元素表示未初始化、无效、未指定货默认状态

          使用None、Unspecified、UnKnown或Invalid之类的名字定义一个枚举元素,为可变枚举的有效性验证提供类型安全的方法。

    10.4 验证枚举值

          可以使用Enum.IsDefined()来判断枚举中是否存在某个值。但是,此函数是一种昂贵的远行时函数调用。

    11.类型安全、强制转换与转换

    11.1 避免强制转换,并且不要强迫别人使用强制转换

          在类型之间转换对象时,尽量使用System.Convert类中的类型安全函数,而不是直接强制转换。

    11.2 as操作符胜过直接强制转换

          如果对象实例与给定类型不兼容,则直接强制转换将抛出一个InvalidCastException异常。C#的as操作符将尝试把对象强制转换为指定类型,如果失败则返回null。

    11.3 使用多态而不频繁使用is或as

    12. 错误处理和调试

    12.1 使用返回码报告预期的状态改变

    12.2 使用异常强迫获得编码契约

    12.3 不要静默地接受或忽略非预期的运行时错误

    12.4 使用断言或异常报告非预期的活未处理的运行时错误

    12.5 使用异常来报告可恢复错误

    12.6 使用try..finally 代码块或using语句管理资源

    12.7 尽可能抛出最具体的异常

    12.8 只捕获你能处理的异常

    12.9 如果在catch代码中抛出新异常,不要丢弃异常信息

    12.10 依异常类型特殊性级别排列catch块

    12.11 不要在finally块中抛出异常

    12.12 仅在认为调用者会做特别处理时才创建自定义异常

    12.13 从ApplicationException而不是Exception派生自定义异常

    12.14 使用内建的调试类调试代码

          Debug类提供了许多有用的方法打印诊断信息和检查逻辑。

    13. 事件、委托和线程

    13.1 使用lock()而不是Monitor.Enter(0

          lock语句提供了一种获取对象互斥锁的捷径,且能确保锁会被正确释放。

       

    lock(x) ...
    等价于
    System.Threading.Monitor.Enter(x);
    try
    {
    ...
    }
    finally
    {
    System.Threading.Monitor.Exit(x);
    }

    13.2 只锁定私有对象

            

  • 相关阅读:
    csp-s模拟110
    csp-s模拟109
    留念
    csp-s 2019 游记
    HEOI2020
    CSP-S2019记
    堆积的$TIPS$
    低错复习
    倍增并查集
    4.26
  • 原文地址:https://www.cnblogs.com/lufangtao/p/2657468.html
Copyright © 2011-2022 走看看