《Effective C#》读书笔记——条目16:避免创建非必要的对象<.NET资源管理>

简介:

我们知道:C#是一门虚拟机语言,C#编译器首先将C#代码编译成IL代码,运行程序时CLR(Common Language Runtime,公共语言运行时)通过调用JIT(just-in-time Compiler即时编译器)来将IL代动态即时编译成可执行的机器码。GC(Garbage Collector,垃圾收集器)自动为我们的应用程序进行内存管理的分配和释放,(具体参见:了解.NET 内存管理机制),以一种高效的方式来移除内存中的垃圾对象,不过不管有多高效,分配和销毁在堆上的对象总会花费掉时间。

  如果我们在一个方法中创建了过多的引用对象,会对应用程序的性能产生严重的影响。因此我们应该遵守下面的一些规则,可以尽量的降低GC的工作量。

 

阅读目录:

      1.将常用的局部变量提升为成员变量

      2.为常用的类型实例提供静态对象

      3.为不可变类型提供可变的创建对象

      小节

 

1.将常用的局部变量提升为成员变量

   所有的引用类型,包括那些局部变量,都会分配到堆上。在函数退出后,函数内的所有局部变量都会立即变成垃圾对象。所以我们可以得出结论:

若是某个引用类型(值类型无所谓)的局部变量用于被频繁调用的例程中,那么应该将其提升为成员变量。这既有助于减轻GC的负担,也可以提升程序运行的效率。

 在GUI编程中一个常见的错误就是:在窗体的Paint处理函数中创建GDI(Graphics Device Interface,图形设备接口)对象,如下:

复制代码
1         protected override void OnPaint(PaintEventArgs e)
2         {
3             using (Font myFont = new Font("Arial", 10.0f))
4             {
5                 e.Graphics.DrawString(DateTime.Now.ToString(), myFont, Brushes.Black, new Point(0, 0));
6             }
7             base.OnPaint(e);
8         }
复制代码

 

OnPaint()将会被非常频繁的调用,每次调用都会创建一个Font对象,而包含的内容完全和上一次一样。所以GC需要每次都为你清扫这些垃圾,严重影响了应用程序的效率。其实我们完全可以将Font对象提升为成员变量,是每次窗体重绘时能够重用该Font对象:

复制代码
1         private readonly Font myFont = new Font("Arial", 10.0f);
2 
3         protected override void OnPaint(PaintEventArgs e)
4         {
5                 e.Graphics.DrawString(DateTime.Now.ToString(), myFont, Brushes.Black, new Point(0, 0));
6 
7                 base.OnPaint(e);
8         }
复制代码

 将常用的局部变量提升为成员变量之后,程序无需每次重绘时生成垃圾对象,减轻了GC的负担,也提升了程序的效率。不过这里有一个小小的限制:将实现了IDisposable接口的局部变量提升为成员变量,那么这个成员变量依附类本身也需要实现IDisposable接口。

 

2.为常用的类型实例提供静态对象

  静态成员变量可以让引用类型在类的各个实例中共享。我们可以通过提供了一个类,存放某个类型常用的实例的单例对象,这样可以避免创建重复的对象。.NET Framework 的类库中就有很多这样的做法:Brushes类包含了一系列的静态Brush对象,每个都包含了一种常用的颜色。它们的简要实现如下:

复制代码
 1         private static Brush blackBrush;
 2         public static Brush Black
 3         {
 4             get
 5             {
 6                 if (blackBrush == null)
 7                     blackBrush = new SolidBrush(Color.Black);
 8                 return blackBrush;
 9             }
10         }
复制代码

在第一次请求黑色画刷时,Brushes将创建一个实例,随意Brushes类将保留该实例的引用,并在后续的请求时直接返回同一个句柄。也就是说我们只创建了一个黑色画刷,然后一直重用这个对象。至于其他的例如:红色的画刷,如果应用程序没有使用这个资源那么该对象也不会被分配。.NET 提供的这种方式能够在满足需求的前提下尽可能的少创建对象,我们在自己的应用程序中也应该这样做。

 

3.为不可变类型提供可变的创建对象

  System.String类型时一个不可变类型:即在构造一个字符串对象后,其内容不能被修改。如果对一个字符串进行修改时,实际上时创建了一个新的字符串对象,从前的字符串对象也就变成了垃圾。看下面的代码:

1             //How are you?
2             string msg = "How";
3             msg += " are";
4             msg += " you";
5             msg += "?";

 

string类型的+=操作符会创建一个新的字符串对象并返回,对于这类拼接字符串的工作应该交给更适合的string.Format()方法:

1 string msg = string.Format("{0} {1} {2}{3}", "How", "are", "you", "?");

 

如果需要进行一些比简单拼接字符串更加复杂的工作,可以考虑使用StringBuilder类,该类是一个可变的字符串,用来创建不可变的string对象。对于经常变化的stirng对象,使用StringBuilder对象来替换是一个非常好的选择 —— 当我们的某个设计需要不可变类型时,应该考虑提供一个创建对象,专门负责分步地构造出最终对象(例如:string对象之于StringBuilder对象)。这样既可让用户分步地创建出最终对象,也可以保证对象的不可变性

 

小节

  GC可以高效的管理应用程序使用的内存,不过创建和销毁堆上的对象仍旧需要时间,因此,应该避免创建过多的对象,不要创建那些非必要的对象,也不要在局部方法中创建太多的引用对象,可以考虑将常用的局部变量提升为成员变量,或者为最常用的类型实例提供静态对象,此外还可以考虑为不可变类型提供可变的创建对象。

本文转自gyzhao博客园博客,原文链接:http://www.cnblogs.com/IPrograming/archive/2012/11/27/EffectiveCSharp_16.html ,如需转载请自行联系原作者
相关文章
|
Web App开发 C# Windows
一款.NET开源的Windows资源管理器标签页工具
一款.NET开源的Windows资源管理器标签页工具
230 5
《More Effective C# 》读书笔记 第一章
《More Effective C# 》读书笔记 第一章
205 0
|
缓存 算法 Java
《深入理解Java虚拟机》读书笔记(四)--GC的回收条件及Java对象的引用
《深入理解Java虚拟机》读书笔记(四)--GC的回收条件及Java对象的引用
360 0
|
C++ 容器 存储
读书笔记 effective c++ Item 28 不要返回指向对象内部数据(internals)的句柄(handles)
假设你正在操作一个Rectangle类。每个矩形可以通过左上角的点和右下角的点来表示。为了保证一个Rectangle对象尽可能小,你可能决定不把定义矩形范围的点存储在Rectangle类中,而是把它放入一个辅助结构体中,Rectangle中声明一个指向它的指针就可以了: 1 class...
1031 0
|
C++
读书笔记 effective c++ Item 21 当你必须返回一个对象的时候,不要尝试返回引用
1. 问题的提出:要求函数返回对象时,可以返回引用么? 一旦程序员理解了按值传递有可能存在效率问题之后(Item 20),许多人都成了十字军战士,决心清除所有隐藏的按值传递所引起的开销。对纯净的按引用传递(不需要额外的构造或者析构)的追求丝毫没有懈怠,但他们的始终如一会产生致命的错误:它们开始传递指向并不存在的对象的引用。
1075 0
|
API C++
读书笔记 effective c++ Item 15 在资源管理类中提供对原生(raw)资源的访问
1.为什么需要访问资源管理类中的原生资源  资源管理类是很奇妙的。它们是防止资源泄漏的堡垒,没有资源泄漏发生是设计良好的系统的一个基本特征。在一个完美的世界中,你需要依赖这样的类来同资源进行交互,绝不要直接访问原生(raw)资源而玷污你的双手。
1105 0