[.NET] 《Effective C#》快速笔记(二)- .NET 资源托管

《Effective C#》快速笔记(二)- .NET 资源托管

图片 1

 

简介

  续 《Effective C#》读书笔记(一)- C#
语言习惯。

  .NET 中,GC
会帮助我们管理内存,我们并不需要去担心内存泄漏,资源分配和指针初始化等问题。不过,它吧不要万能,因为非托管资源用我们好开展清理,如文件句柄、数据库连接、GDI+
对象以及COM 对象等。

 

目录

  • 十二、推荐应用成员初始化器而非是赋值语句
  • 十三、正确地初始化静态成员变量
  • 十四、尽量减少重复的初始化逻辑
  • 十五、使用 using 和 try/finally 清理资源
  • 十六、避免创建非必不可少之靶子
  • 十七、实现正式的绝迹模式
  • 十八、区分值类型和援类型
  • 十九、保证 0 为值类型的实惠状态
  • 二十、保证值类型的常量性和原子性

 

十二、推荐用成员初始化器而未是赋值语句

  1.分子初始化器:在宣称变量时即进行初始化,而非是于每个构造函数中进行。

  2.坐下 3 栽状态,应避免采取成员初始化器:

  (1)当你想只要初始化对象也 0 或 null
时。因为系统默认的初始化工作(在具有代码执行前)会用全方位设置为 0 或
null,我们开的是同等步多余的操作。而且,如果是值类型,那么性能特别不同。

            MyValueType myVal1; //初始化为 0
            MyValueType myVal2 = new MyValueType(); //也是 0

  这片长达告句都用变量初始化为 0,但首先长条凡透过安装包含 myVal1
的马上等同块内存为 0 实现的,而第二漫长使用的是 initobj 这漫漫 IL 指令,导致了针对
myVal2 变量的一律坏装拆箱操作,这将占额外性能及时。

  (2)需要对同一个变量执行不同的初始化方式:

    class Program
    {
        /// <summary>
        /// 声明并初始化
        /// </summary>
        private List<string> _lables = new List<string>();

        public Program() { }

        public Program(int capacity)
        {
            _lables = new List<string>(capacity);
        }
    }

  初始化该类时,假如下的是拉动 capacity 的构造函数,那么
List<string> 对象表示初始化了 2 次,头一个就是变成了垃圾堆对象。

  (3)将初始化代码放在构造函数的适度理由:可以好好管理
try-catch。

 

十三、正确地初始化静态成员变量

  1.以用类的实例之前,就应初始化该类型的备静态成员变量。静态构造函数是一个新鲜的函数,将当旁具有办法、变量或性能被第一次访之前实施。你得行使这函数来初始化静态变量和兑现单例模式等操作。

  2.静态初始化器和静态构造函数是初始化类的静态成员的特级选项。

  3.使静态构造函数而不是静态初始化器最广大的说辞是得捕捉和拍卖非常。

 

十四、尽量减少重复的初始化逻辑

  1.设多单构造函数包含类似的逻辑,我们承诺将那提及一个国有的构造函数中,这样可以避代码重复,也得以用构造函数初始化器生成又敏捷的代码。

    class MyClass
    {
        private List<string> _lables;
        private string _name;

        public MyClass() : this(0, string.Empty)
        {

        }

        public MyClass(int capacity = 0, string name = "")
        {
            _lables = new List<string>(capacity);
            _name = name;
        }
    }

  第二个构造函数使用了 “” 来吃出 name 的默认值,而非是使
string.Empty ,因为 string.Empty 并无是一个编译期的常量,而是一个概念在
string 类中之静态属性,所以不能够为此作参数的默认值。

  2.创办有项目的第一单实例时所开展的操作顺序:

  (1)静态变量设置也 0 ;

  (2)执行静态变量初始化器;

  (3)执行基类的静态构造函数;

  (4)执行静态构造函数;

  (5)实例变量设置为 0;

  (6)执行实例变量初始化器;

  (7)执行基类中宜的实例构造函数;

  (8)执行实例构造函数。

  3.使用初始化器来初始化简单的资源,使用构造函数来初始化需要复杂逻辑的积极分子,同事不要遗忘将调用抽取到一个构造函数中,以便减少重复。

  4.构造函数定义着不得不采取一个初始化器,要么以 This()
委托为其他一个构造函数,要么下 base() 调用基类的构造函数。

 

十五、使用 using 和 try/finally 清理资源

  1.动了非托管系统资源的种必须出示地应用 IDisposable 接口的
Dispose() 来刑释解教,Using() 语句以非常成一个Try/finally 块。

 

十六、避免创建非必不可少的对象

  1.GC
可以老好地管理内存,但不论是多快,分配和销毁堆上之对象总会花好丰富日子,如果过多的开创引用对象,那么会指向先后的性质有严重的影响。

        public void Paint()
        {
            using (var myFont = new Font("Arial", 10.0f))
            {
                Console.WriteLine($"使用 {myFont} 进行绘画");
            }
        }

  假如该方法被杀频繁地调用。每次调用时犹见面创另一个 Font
对象,但其包含的情及事先的是全同。GC
每次都如吧而清理这些废品,显然是坏低效的。

  可以管 myFont 提升为静态变量。

        private readonly static Font _myFont = new Font("Arial", 10.0f);

        public void Paint()
        {
            Console.WriteLine($"使用 {_myFont} 进行绘画");
        }

  2.降低程序中开创目标数量的计。

  (1)将常用的组成部分变量提升为成员变量;

  (2)提供一个类,存放有项目常用实例的单例对象。

   3.为此 StringBuilder 进行复杂的字符串操作

 

十七、实现规范的销毁模式

  1.IDisposable.Dispose() 方法的兑现中待做到如下 4 独任务:

  (1)释放具有非托管资源;

  (2)释放具有托管资源,包括自由事件监听程序;

  (3)设定一个态标志,表示该对象就给销毁;

  (4)跳了了操作,调用 GC.SuppressFinalize(this) 即可。

 

十八、区分值类型和援类型

  1.相似的话,我们创建的多数凡是援引类型。

  2.确定创建值类型的极来 4 单 

  (1)该项目的主要职责在于数量存储;

  (2)该种的公有接口都是由访问该数额成员属性定义之吧?

  (3)你规定该类型绝不会有派生类型也?

  (4)你规定拖欠种永远都非欲多态支持为?

  3.据此值类型表示底层存储数据的品类,用引用类型来封装程序的表现。

  4.比方你对品种未来的用处不确定,应选择引用类型。

 

十九、保证 0 为值类型的行状态

  1..NET 系统的默认初始化过程会以有所的靶子设置也 0,建议以 0
作为枚举类型的默认值。

  2.枚举(enum)必须以 0
设定为枚举值的一个卓有成效选择。所有的枚举值都派生自
System.ValueType。枚举的默认值开始为 0。

  3.于开创于定义枚举值时,请确保 0
是一个实惠的选项。若你定义之是标识(flag),那么可拿 0
定义为无入选任何的表明。

    enum Week
    {
        None = 0,
        Monday = 1,
        Tuesday = 2,
        Wednesday = 3,
        Thursday = 4,
        Friday = 5,
        Saturday = 6,
        Sunday = 7
    }

 

二十、保证值类型的常量性和原子性

  1.常量性:自创始后,其值保持无变换。因为未可知转内部状态,就得省去许多请勿必要之一无是处检查,它为是线程安全之,也可高枕无忧地表露于外界,因为调用者不能够转目标的内状态。

  2.设计常量类型时,要保管无外漏洞会造成内状态为外界更改。因为值类型不可知派生,所以不必担心会遭受派生类影响。

  不过,如果常量中凡是可变的援类型字段的话,我们不怕当对这些不过变类型进行防御性的复制。

    class MyClass
    {
        private readonly object[] _objs;

        public MyClass(ICollection<object> objs)
        {
            _objs = new object[objs.Count];
            objs.CopyTo(_objs, 0);  //复制
        }

        public IEnumerable<object> Objs => _objs;
    }

 

        static void Main(string[] args)
        {
            var objs = new object[10];
            var myClass = new MyClass(objs);
            objs[1] = "hi";

            Console.WriteLine(myClass.Objs.ToArray()[1]);

            Console.Read();
        }

  因为数组是援引类型,如果未采用 CopyTo 复制一个副本的话,在表面的
objs 修改就见面一直影响 MyClass 中之
_objs,因为他俩本着的且是与一个引用。

  2.绝不盲目地对准各级一个性质都长 { get; set; }。

 

本系列

  《Effective C#》快速笔记(一)- C#
语言习惯

  《Effective C#》快速笔记(二)- .NET
资源托管

  《Effective C#》快速笔记(三)- 使用 C#
表达设计

  《Effective C#》快速笔记(四) –
使用框架

  《Effective C#》快速笔记(五) – C#
中之动态编程

  《Effective C#》快速笔记(六) – C#
高效编程要点补充

 

 


【博主】反骨仔

【原文】http://www.cnblogs.com/liqingwen/p/6761409.html