【持续更新】提高C#编程水平的48个要点

【持续更新】提高C#编程水平的48个要点

作为做了快五年的C#开发程序员,有必要思考一下如何提升自己的能力。我认为提升能力的维度就两个(代码可读性高、执行效率高),以下是提高C#编程水平的48个要点,我再每一个要点详细展开加入个人的理解。

1.总是用属性 (Property) 来代替可访问的数据成员

可读性高、扩展性强。

2.在 readonly 和 const 之间,优先使用 readonly

readonly为运行时常量,程序运行时进行赋值,赋值完成后便无法更改,因此也有人称其为只读变量。
const为编译时常量,程序编译时将对常量值进行解析,并将所有常量引用替换为相应值。

3.在 as 和 强制类型转换之间,优先使用 as 操作符

因为强制类型转换有可能会因为某个条件下转换失败,而使用 as 操作符,转换失败的结果则为null,可以避免程序报错,并能够进行判断进行对应的操作。

4.使用条件属性 (Conditional Attributes) 来代替条件编译语句 #if

使用Conditional属性可以比使用#if/#endif生成更高效的IL代码。在专门针对函数时,它更有优势,它会强制你在条件代码上使用更好的结构。编译器使用Conditional属性来帮助你避免因使用#if/#endif而产生的常见的错误。条件属性比起预处理,它为你区分条件代码提供了更好的支持。

5.总是为自定义类重载 ToString 方法

System.Object.ToString()恐怕是.NET中最常用的方法了。自定义类在ToString时,返回的结果并非是我们想要的,因此我们在自定义类重载ToString方法,让自定义类在ToString的时候返回其有作用的值。示例如下:

    
    public class Customer
    {
        private string Name { get; set; }
        private string Phone { get; set; }

        public override string ToString()
        {
            return Name;
        }
    }

调用ToString方法时,返回名称

6.区别值类型和引用类型

个人认为最大的区别就是分配在堆栈的问题。
详情:https://blog.csdn.net/qq_59943770/article/details/120802499

7.使用不可变的值类型(Immutable Atomic Value Types)

这个我不太懂是什么意思。

8.在值类型中,确保0是一个合法的数据

主要是为了避免不必要的错误,如enum枚举类型,不从0开始定义的话有时候会造成意想不到的错误。
正确:

    public enum ERROR_CODE
        {
            NO_ERROR                = 0,
            INVALID_FILE_NAME       = 1001,
            CANT_BE_ACCESS          = 1002,
        }

错误:

    public enum ERROR_CODE
        {
            INVALID_FILE_NAME       = 1001,
            CANT_BE_ACCESS          = 1002,
        }

9.理解 ReferenceEquals, Equals 和 比较运算符(==)之间的关系

简单来说:
ReferenceEquals:即Object.ReferenceEquals(param1,param2),是用于判断两个对象ID是否相等。
Equals:即Object.Equals(),静态的Equals()是使用左边参数实例的Equals()方法来断定两个对象是否相等。
==:即用于判断两个值类型的内容是否相等。

10.理解 GetHashCode方法的缺陷

效率低

11.在编写循环时,优先使用 foreach

1.写法简单
2.不需要通过索引获取容器中的元素

12.在定义变量的时候就将其初始化

为了更好的作为判断、避免不必要的错误,如Main方法中定义变量:

public class A
{
	public void Main()
	{
		private B b = new B();
		private string str = "";
		private string str = null;
	}
}
public class B
{
	public string Str {get;set;}
}

13.使用静态构造函数来初始化静态成员变量

总的来说,这样的写法的好处就是可读性高、出错率小。如:

public class MySingleton
{
 private static readonly MySingleton _theOneAndOnly;
 static MySingleton()
 {
  _theOneAndOnly = new MySingleton();
 }
 public static MySingleton TheOnly
 {
  get{ return _theOneAndOnly; }
 }

 /// 
 /// 避免从外部创建对象的私有构造函数
 /// 
 private MySingleton()
 {}
}

14.用多个构造函数时,利用构造函数链

这样的做法就是让一个接受参数最多的构造函数做"主构造函数",且在主构造函数中实现必须的业务逻辑,其余的构造函数只要使用this关键字把传入的参数转发给主构造函数,并且提供必须的其它参数,这样子我们整个类中需要我们操心的就是那个主构造函数了,其余构造函数基本上可以为空。
:this()/:this(params)是继承构造函数

public class Person
{        
        public string personName;
        //定义年龄为可空类型,这样就可以赋予其null值
        public int personAge;

        //下面前三个构造函数都是去调用参数最多的第四个构造函数,只取它们所需要的部分参数即可
        //这样的做法就是this串联构造函数
        public Person():this("",0)
        {}

        public Person(string name):this("evan",null)
        { }

        public Person(int age):this("",20)
        { }

        public Person(string name, int? age)
        {
            this.personName = name;
            //通过判断传入的age是否null值
            //如果属于null值,则赋值100
            this.personAge = age;
        }

        public void Display()
        {
            Console.WriteLine("Name:{0},Age:{1}\n", personName, personAge);
        }        
}

主函数调用如下:

static void Main(string[] args)
{
        Person per1 = new Person();
        per1.Display();           

        Person per2 = new Person(20);
        per2.Display();           

        Person per3 = new Person("evan");
        per3.Display();           

        Person per4 = new Person("evan", 20);
        per4.Display();

        Console.ReadLine();            
}

15.使用using和try/finally来处理资源的释放

这个比较容易理解,使用using和try/finally,当里面的代码执行完后,则会释放资源,这是性能上的调优。
小知识:using实质在程序编译阶段,编译器会自动将using语句生成为try-finally语句,并在finally块中调用对象的Dispose方法,来清理资源。 所以,using语句等效于try-finally语句

16.尽量避免产生资源垃圾

意思是尽量少的new对象(能全局使用就不局部多次new对象使用),因为每次new对象都会占用堆的资源并产生垃圾,GC机制在释放并回收资源是需要消耗CPU,而当需要释放更多的堆栈内存并回收时,则需要消耗更多的CPU。

17.尽量避免使用装箱(boxing)和拆箱(unboxing)

原因有两个,主要是关于效率问题。一个就是对于堆的操作效率比较低;另一个就是对于堆上分配的内存资源,需要GC来回收,从而降低程序效率。

18.实现类的 Dispose 方法

总结:资源分为托管资源(由CLR管理,也就是我们new出来的对象)、非托管资源(Windows内核对象、文件操作、数据库连接、socket、网络等)。实现类的Dispose方法,可以有效、及时的释放资源,更好的利用资源。
详情请看https://zhuanlan.zhihu.com/p/244894004

19.在接口和继承(Inheritance)之间,优先使用接口(interface)

接口是一种约束,优先使用接口的好处在于可以便于管理、调用,而且接口是可以多继承的。

20.区分接口和重载(overrides)

接口:是对继承类的约束、规范。
重载:是同一个方法名,但不同参数。

21.用委托(delegate)来实现回调(callback)

通过定义委托,并规定参数个数、参数类型,方便在回调函数实现业务逻辑。

22.用事件(event)来定义外部接口

不太理解。

23.避免返回类内部成员的引用

24.使用元数据来控制程序

25.优先使用可序列化(serilizable)类型

26.对需要排序的对象实现IComparable和IComparer接口

27.避免使用 ICloneable接口

28.避免使用类型转换操作符

29.只有当基类加入了与派生类中现有的函数名称相同的函数时,才需要使用 new 操作符

30.尽量使用 CLS-Compliant

31.尽量编写短少,简单的函数

32.尽量编写比较小的程序集(assembly)

33.限定类型的可见性(visibility)

34.编写大粒度的 web API

35.在使用事件时,优先继承基类事件,而不是重新创建一个事件

36.多使用 framework 的运行时调试 (DEBUG, TRACE, EVENTLOG等)

37.使用.net标准的配置机制

38.使用并且在类中支持.net的数据绑定功能 (Data Binding)

39.使用.net的验证机制 (Validation)

40.根据你的需求选择正确的集合类(Collection)

41.在自定义结构中使用 DataSet

42.利用属性(Attributes)

43.不要过度使用反射(Reflection)

44.创建完整的,应用程序特定的异常

45.尽可能多的考虑程序可能出现的异常,并作出处理

46.尽可能少的使用 Interop

47.尽量使用安全代码 (safe code)

48.多多学习、使用外部工具和资源

你可能感兴趣的:(C#,c#,开发语言)