上一节介绍到了如何进行Object方法重写和操作符重载,本篇博客来接着介绍合式类型剩余的内容:

程序集引用及命名空间定义

其中一些相对简单的内容就不进行过多的介绍了,例如引用其他程序集,只需要注意三种引用方式:

  • 第一种方式是引用库项目文件,指出库的源代码在哪个项目中,并在两个项目之间建立依赖关系。编译好库之后才能编译引用了该库的程序。该依赖关系造成在编译程序时先编译库(如果还没有编译的话)项目拷贝,常用
  • 第二种方式是引用程序集文件本身。换言之,引用编译好的库而不是项目。如果库和程序分开编译,比如由企业内的另一个团队编译,这种方式就非常合理。(dll拷贝,调试时候用)
  • 第三种方式是引用NuGet包项目拷贝高级版,从NuGet库拷贝项目

注意库和包并非只能由控制台程序引用。事实上,任何程序集都能引用其他任何程序集。经常是一个库引用另一个库,创建一个依赖链。同时可以关注下类型的访问级别:

需要注意:成员的可访问性不能大于其包容类类(除了嵌套类,其可使用任意修饰符)只能使用public和internal。命名空间比较简单,只需要注意如下设计规范就行了:

  • 为命名空间附加公司名前缀,防止不同公司使用同一个名称。
  • 要为命名空间二级名称使用稳定的、不随版本升级而变化的产品名称
  • 不要定义没有明确放到一个命名空间中的类型。
  • 考虑创建和命名空间层次结构匹配的文件夹结构

总而言之就是,最好一个类在一个文件,用一个命名空间包裹一个类。

XML注释

虽然C#编译器在最终生成的可执行文件中忽略所有注释,但开发者可利用命令行选项,指示编译器将XML注释提取到单独的XML文件中。这样就可根据XML注释生成API文档。此外,C#编辑器可解析代码中的XML注释,并对其进行分区显示(例如,可使用有别于其他代码的一种颜色),或者解析XML注释数据元素并向开发者显示。这样就可以进行文档规范化操作,例如可以定义一些参数,生成API文档的时候予以解析

垃圾回收

垃圾回收是“运行时”的核心功能,旨在回收不再被引用的对象所占用的内存。这句话的重点是**“内存”和“引用”垃圾回收器只回收内存,不处理其他资源,比如数据库连接、句柄(文件、窗口等)、网络端口以及硬件设备(比如串口)。此外,垃圾回收器根据是否存在任何引用来决定要清理什么。这暗示垃圾回收器处理的是引用对象,只回收堆上的内存。另外,还意味着假如维持对一个对象的引用,就会阻止垃圾回收器重用对象所用的内存。**

垃圾回收算法

.Net使用mark-and-compact算法,就是先把可达对象整理和覆盖不可访问对象内存,然后执行清理,整个操作流程如下,使用可达性算法进行:

  1. 一次垃圾回收周期开始时,识别对象的所有根引用。根引用是来自静态变量、CPU寄存器以及局部变量或参数实例(包括f-reachable对象)的任何引用基于该列表,垃圾回收器可遍历每个根引用所标识的树形结构,并递归确定所有根引用指向的对象。这样,垃圾回收器就可识别出所有可达对象
  2. 执行垃圾回收时,垃圾回收器不是枚举所有访问不到的对象;相反,它将所有可达对象紧挨着放到一起,从而覆盖不可访问的对象(也就是垃圾,或者不可达对象)所占用的内存,为定位和移动所有可达对象,系统要在垃圾回收器运行期间维持状态的一致性。为此,进程中的所有托管线程都会在垃圾回收期间暂停。这显然会造成应用程序出现短暂的停顿。不过,除非垃圾回收周期特别长,否则这个停顿是不太引人注意的。为尽量避免在不恰当的时间执行垃圾回收,System.GC对象包含一个**Collect()**方法。可在执行关键代码之前调用它(执行这些代码时不希望GC运行)。这样做不会阻止垃圾回收器运行,但会显著减小它运行的可能性——前提是关键代码执行期间不会发生内存被大量消耗的情况
  3. 发现相较于长期存在的对象,最近创建的对象更有可能需要垃圾回收。为此,.NET垃圾回收器支持“代”(generation)的概念,它会以更快的频率尝试清除生存时间较短的对象(新生对象)。而那些已在一次垃圾回收中“存活”下来的对象(老对象)会以较低的频率清除。具体地说,共有3代对象。一个对象每次在一个垃圾回收周期中存活下来,它都会移动到下一代,直至最终移动到第二代(从第零代开始)。相较于第二代对象,垃圾回收器会以更快的频率对第零代的对象执行垃圾回收

相较于Java的成体系的垃圾回收算法,感觉CLR做的不是很好,也许是没有体系化的了解过吧。

弱引用

弱引用是什么?弱引用就是一直维持的一个引用,但是它并不会组织垃圾回收,为什么要有弱引用呢?这么去想象一下,假如要从数据库加载一个特别大的对象,如果是强引用,一旦用户不再使用该对象,断开引用就需要进行垃圾回收,但假如用户一会儿又想用了,又得重新加载和引用,但如果是弱引用,你可以在保持引用的状态下进行垃圾回收,但在你垃圾回收之前我还是一直保持引用状态,那么假如用户下次请求的时候刚好CLR还没有回收对象,就可以直接获取到对象了,实际上相当于一次内存里的缓存

 public class Program{private WeakReference Data;public FileStream GetData(){FileStream data = (FileStream)Data.Target;if(data != null){return data;}else{// Load data// ...// Create a weak reference// to data for use laterData.Target = data;}return data;}public static void Main(){Console.WriteLine("No output in this example.");}}

创建弱引用(Data)之后,可查看弱引用是否为null来检查垃圾回收。但这里的关键是先将弱引用赋给一个强引用(FileStream data=Data),避免在“检查null值”和“访问数据”这两个动作之间,垃圾回收器运行并清除弱引用。强引用明显会阻止垃圾回收器清除对象,所以它必须先被赋值(而不是先检查Target是不是为null)

终结器

我们知道垃圾回收器不负责处理除了堆上的引用资源,那么除了内存管理外的一些数据库连接以及句柄等这些资源该怎么释放呢,这个时候就要用到终结器,同时终结器在垃圾回收前也发挥着不可替代的作用(虽然会降低性能,但是能保证对象被延迟清除)

class TemporaryFileStream{public TemporaryFileStream(string fileName){File = new FileInfo(fileName);Stream = new FileStream(File.FullName, FileMode.OpenOrCreate,FileAccess.ReadWrite);}public TemporaryFileStream(): this(Path.GetTempFileName()){ }// Finalizer~TemporaryFileStream(){Close();}public FileStream Stream { get; }public FileInfo File { get; }public void Close(){Stream?.Dispose();File?.Delete();}}
}

上边这个终结器用来删除文件和关闭流。需要注意的几点是:

  • 终结器不能被显式调用,只能通过垃圾回收器调用,会在对象最后一次使用之后调用,开发人员只能定义不能调用
  • 终结器不允许传递任何参数,不可重载,不可添加访问修饰符

这里的dispose一个对象不是垃圾回收一个对象,而是释放该对象中包装的资源,例如它字段所引用的对象,解除引用,具体的垃圾回收还是需要垃圾回收器的。这里有个漏洞会导致Dispose()执行不了,也就是在实例化TemporaryFileStream后,调用Dispose()前如果发生异常可能不会调用Dispose(),这个时候就要使用确定性终结

 public static void Search(){using(TemporaryFileStream fileStream1 =new TemporaryFileStream(),fileStream2 = new TemporaryFileStream()){// Use temporary file stream;}}

相当于包裹了一个try finally块。所以实现了终结器的方法的对象的垃圾回收流程是这样的(如果终结器不调用System.GC.SuppressFinalize):整体流程是:先由可达性分析算法把待清理的对象引用放到f-reachable队列里,然后由终结器进行引用终结,终结完成后垃圾回收器才对对象进行垃圾回收。需要注意以下几点:

  • 要只为使用了稀缺或昂贵资源的对象实现终结器方法,即使终结会推迟垃圾回收。
  • 要为有终结器的类实现IDisposable接口以支持确定性终结
  • 要为实现了IDisposable的类实现终结器方法,以防Dispose()没有被显式调用。
  • 要重构终结器方法来调用与IDisposable相同的代码,可能就是调用一下Dispose()方法。
  • 不要在终结器方法中抛出异常
  • 要从Dispose()中调用System.GC.SuppressFinalize(),使垃圾回收更快地发生,并避免重复性的资源清理
  • 要保证Dispose()可以重入(可被多次调用)。
  • 要保持Dispose()的简单性,把重点放在终结所要求的资源清理上。
  • 避免为自己拥有的、带终结器的对象调用Dispose()。相反,依赖终结队列清理实例。
  • 避免在终结方法中引用未被终结的其他对象(对象复活,在f-reachable队列中又被外部引用了)。
  • 要在重写Dispose()时调用基类的实现。
  • 考虑在调用Dispose()后将对象状态设为不可用。对象被dispose之后,调用除Dispose()之外的方法应引发ObjectDisposedException.异常。(Dispose()应该能多次调用。)
  • 要为含有可dispose字段(或属性)的类型实现IDisposable接口,并dispose这些字段引用的对象

可以看到一个对象被回收会经历多么复杂的过程,那么我们最好让他在需要出现的时候再初始化岂不更好:

 using AddisonWesley.Michaelis.EssentialCSharp.Chapter10.Listing10_21;class DataCache{// ...public TemporaryFileStream FileStream{get{if (_FileStream == null){_FileStream = new TemporaryFileStream();}return _FileStream;}}private TemporaryFileStream _FileStream = null;// ...}

只有调用FileStream属性的get方法时才加载TemporaryFileStream。

总结一下,其实也就是程序集如何加载,命名空间使用规范,垃圾回收如何执行以及终结器(析构函数),它的好处和缺点。

【C#本质论 十一】合式类型(二)程序集引用、XML注释、垃圾回收和资源清理相关推荐

  1. C#问题——CS1591 缺少对公共可见类型或成员的 XML 注释

    声明:本文为个人笔记,用于学习研究使用非商用,内容为个人研究及综合整理所得,若有违规,请联系,违规必改. C#问题--CS1591 缺少对公共可见类型或成员的 XML 注释 文章目录 C#问题--CS ...

  2. 深入理解JVM——(二)搞定JVM垃圾回收就是这么简单

    一.前言:JVM区域 在学习GC之前,先搞懂JVM区域.JVM分为两大区域,deap区和非deap区,即堆与非堆. deap区: Eden Space(伊甸园) Survivor Space(幸存者区 ...

  3. 引用“.NET研究”类型赋值为null与加速垃圾回收

    在标准的Dispose模式中,提到了需要及时释放资源,却并没有进一步细说让引用等于null是否有必要. 有一些人认为等于null可以帮助垃圾回收机制早点发现并标识对象是垃圾.其他人则认为这没有任何帮助 ...

  4. C# Nut Shell 第十二章 销毁对象与垃圾回收

    IDisposable接口.Dispose方法和Close方法 using语句提供了调用实现了IDisposable接口对象的Dispose方法的快捷方法.相当于实现了try/finally语句 标准 ...

  5. Java内存分配与垃圾回收(二)

    写在前面:主要为<深入理解Java虚拟机>的读书笔记,加上自己的思考,本篇主要讲垃圾回收,图片主要来自网络,侵删. Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的" ...

  6. JAVA 面试题 合辑(二)

    JAVA 面试题 合辑(一)https://blog.csdn.net/haponchang/article/details/92741553 JAVA 面试题 合辑(二)https://blog.c ...

  7. Linq之隐式类型、自动属性、初始化器、匿名类

    目录 写在前面 系列文章 隐式类型 自动属性 初始化器 匿名类 总结 写在前面 上篇文章是本系列的小插曲,也是在项目中遇到,觉得有必要总结一下,就顺手写在了博客中,也希望能帮到一些朋友.本文将继续介绍 ...

  8. 从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板

    标题:从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p/11155 ...

  9. 【QR Code Generator】开源免费响应式QRcdr二维码生成网站源码

    介绍: 开源免费响应式QRcdr二维码,一款基于PHP编写的二维码在线生成系统,只需点击几下就可以生成您的个人二维码,上传您的徽标或水印,选择自定义颜色,生成多种类型,选择一个图案并下载最终的二维码图 ...

最新文章

  1. 冒烟测试与回归测试的区别
  2. python中浅拷贝与深拷贝
  3. [BZOJ 3173] [TJOI 2013] 最长上升子序列(splay)
  4. vue 无法进入response拦截器_vue拦截器的一次实践
  5. shared_ptr的一些尴尬
  6. 从容器到微服务,技术架构、网络和生态详解
  7. 《R语言编程艺术》——1.4 R语言中一些重要的数据结构
  8. Bug:Google Analytics例子未使用example.com
  9. STVP下载STM8单片机提示Verify error at address 0xxxxx的问题解决
  10. 知网文献nh、caj格式文件转成pdf
  11. 将多名学生成绩绘制在一张画布中,并在图中显示学生成绩
  12. 【固态硬盘】入门讲解
  13. y=asin(wx+φ)的对称中心_函数y=Asin(wx+φ)的图像
  14. 【mac】【转发】Mac系统升级后,按大小写键没反应了,切换大小写的灯不亮了
  15. JSON是什么?JSON字符串是什么?JSON对象又是什么?
  16. 微信小程序开发(十二)富文本插件wxParse的使用
  17. 被讨厌的勇气读书笔记
  18. window系统静默运行批处理
  19. c语言编写一个火车票,C语言-多线程抢火车票软件
  20. DELL服务器U盘安装Centos 7

热门文章

  1. mysql占用内存过高_MySQL内存消耗过高问题处理
  2. Ubuntu折腾记录
  3. Mysql Field * doesn't have a default value解决方法
  4. ABAP VF01 / VF04销售开票增强 增加校验
  5. [转]明朝出了个张居正 作者:秋风浩荡 -3
  6. 打印机不打印计算机原因,打印机打印不完整?是这10个原因造成的!打印必备...
  7. 用python画带有正负值的条形图
  8. Spreadsheet
  9. 「Slack」- 安装 @20210303
  10. kotlin开发Android入门篇八Kotlin开发Android的基本使用