[.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

 

相关文章