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

简介
续 《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(, string.Empty)
{ } public MyClass(int capacity = , 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 = ,
Monday = ,
Tuesday = ,
Wednesday = ,
Thursday = ,
Friday = ,
Saturday = ,
Sunday =
}
二十、保证值类型的常量性和原子性
1.常量性:自创建后,其值保持不变。因为不能更改内部状态,就可以省去许多不必要的错误检查,它也是线程安全的,也可以安全地暴露给外界,因为调用者不能改变对象的内部状态。
2.设计常量类型时,要确保没有任何漏洞会导致内部状态被外界更改。因为值类型不能派生,所以不必担心会受到派生类影响。
不过,如果常量中是可变的引用类型字段的话,我们就应该对这些可变类型进行防御性的复制。
class MyClass
{
private readonly object[] _objs; public MyClass(ICollection<object> objs)
{
_objs = new object[objs.Count];
objs.CopyTo(_objs, ); //复制
} public IEnumerable<object> Objs => _objs;
}
static void Main(string[] args)
{
var objs = new object[];
var myClass = new MyClass(objs);
objs[] = "hi"; Console.WriteLine(myClass.Objs.ToArray()[]); Console.Read();
}
因为数组是引用类型,如果不使用 CopyTo 复制一个副本的话,在外部的 objs 修改就会直接影响 MyClass 中的 _objs,因为他们指向的都是同一个引用。
2.不要盲目地对每一个属性都加上 { get; set; }。
本系列
《Effective C#》快速笔记(一)- C# 语言习惯
《Effective C#》快速笔记(二)- .NET 资源托管
《Effective C#》快速笔记(三)- 使用 C# 表达设计
《Effective C#》快速笔记(五) - C# 中的动态编程
《Effective C#》快速笔记(六) - C# 高效编程要点补充
【博主】反骨仔
【原文】http://www.cnblogs.com/liqingwen/p/6761409.html
[.NET] 《Effective C#》读书笔记(二)- .NET 资源托管的更多相关文章
- C#学习笔记二 (资源托管,泛型,数组和元组,运算符和类型强制转换)
托管和非托管资源 1.托管资源是指GC管理的内存空间,非托管资源是指文件句柄,网络连接,数据库连接等. 2.方法中临时申请的变量,被存放在栈中.栈存储非对象成员的值数据.例如在方法中有B b=new ...
- Effective STL 读书笔记
Effective STL 读书笔记 标签(空格分隔): 未分类 慎重选择容器类型 标准STL序列容器: vector.string.deque和list(双向列表). 标准STL管理容器: set. ...
- Effective STL读书笔记
Effective STL 读书笔记 本篇文字用于总结在阅读<Effective STL>时的笔记心得,只记录书上描写的,但自己尚未熟练掌握的知识点,不记录通用.常识类的知识点. STL按 ...
- webpack笔记二 管理资源
webpack笔记二 管理资源 webpack最出色的功能之一就是除了引入JavaScript,还可以通过loader引入任何其它类型的文件. 加载CSS 为了在JavaScript模块中import ...
- spring揭秘 读书笔记 二 BeanFactory的对象注册与依赖绑定
本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,而且IoC Service Pr ...
- spring揭秘 读书笔记 二 BeanFactory的对象注冊与依赖绑定
本文是王福强所著<<spring揭秘>>一书的读书笔记 我们前面就说过,Spring的IoC容器时一个IoC Service Provider,并且IoC Service Pr ...
- 《你必须知道的.NET》读书笔记二:小OO有大原则
此篇已收录至<你必须知道的.Net>读书笔记目录贴,点击访问该目录可以获取更多内容. 一.单一职责原则 (1)核心思想:一个类最好只做一件事,只有一个引起它变化的原因 (2)常用模式:Fa ...
- how tomcat works 读书笔记(二)----------一个简单的servlet容器
app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/arti ...
- ES6读书笔记(二)
前言 前段时间整理了ES6的读书笔记:<ES6读书笔记(一)>,现在为第二篇,本篇内容包括: 一.数组扩展 二.对象扩展 三.函数扩展 四.Set和Map数据结构 五.Reflect 本文 ...
- effective c++读书笔记(一)
很早之前就听过这本书,找工作之前读一读.看了几页,个人感觉实在是生涩难懂,非常不符合中国人的思维方式.之前也有博主做过笔记,我来补充一些自己的理解. 我看有人记了笔记,还不错:http://www.3 ...
随机推荐
- 3401: [Usaco2009 Mar]Look Up 仰望
3401: [Usaco2009 Mar]Look Up 仰望 Time Limit: 3 Sec Memory Limit: 128 MBSubmit: 136 Solved: 81[Submi ...
- Protobuf 在Ubuntu 14上的编译与使用
前言 一直知道Google开源的一个与语言无关的数据交换协议:protobuf.只知道是一种不同于json和XML的格式,还有就是性能特别的好(这在Java和C++的实现确实是!) 最近闲下来看了下G ...
- 通过Servlet生成验证码图片
原文出自:http://www.cnblogs.com/xdp-gacl/p/3798190.html 一.BufferedImage类介绍 生成验证码图片主要用到了一个BufferedImage类, ...
- linux 私房菜 CH6 Linux 的档案权限与目录配置
查看文件属性 ls -al 第一栏:类型与权限 d:目录: -:档案: l:链接档: b:可随机存取装置: c:一次性存取装置: 第二栏:有多少档名连结到此节点 第三栏:拥有者 第四栏:所属群组 第五 ...
- Ext JS 6学习文档–第1章–ExtJS入门指南
Ext JS 入门指南 前言 本来我是打算自己写一个系列的 ExtJS 6 学习笔记的,因为 ExtJS 6 目前的中文学习资料还很少.google 搜索资料时找到了一本国外牛人写的关于 ExtJS ...
- RxJava Map操作详解
2016-06-06 RxJava是最近两年火起来的一个框架,核心是异步,但是对于我来说印象最深的是响应式编程的思想.最近刚好想把自己的项目改成用RxJava实现,所以就研究了下.抛物线和大头鬼两位大 ...
- 关于数据库与JAVA连接及其出现问题的解决方式
数据库连接java的方式一共有两个 建立JDBC-ODBC桥接器 加载纯JAVA数据库驱动程序 注意: JDBC-ODBC桥接器无需下载数据库的驱动程序,但是JDK1.8已经把JDBC-ODBC桥接器 ...
- 20155304 2016-2017-2 《Java程序设计》第四周学习总结
20155304 2016-2017-2 <Java程序设计>第四周学习总结 教材学习内容总结 第六章 继承: 概念: 面向对象中,为避免多个类间重复定义共同行为.(简单说就是将相同的程序 ...
- WebX框架学习笔记之一
Webx是什么? Webx是一套基于Java Servlet API的通用Web框架.它在Alibaba集团内部被广泛使用.从2010年底,向社会开放源码. Webx的发展历史 2001年,阿里巴巴内 ...
- C#编写代码:求三个数中的最大数
static void Main(string[] args) { float x, y, z, temp; Console.Write(&q ...