析构函数
https://blog.csdn.net/leonwei/article/details/52471026
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GCHandle { class Program { static void Main(string[] args) { test1(); Console.ReadKey(); } static void test1() { Son son = new Son(); son.Age = 32; Console.WriteLine(son.Age); son = null; //GC.Collect(); } public static void write(string connStr) { FileStream fs = new FileStream("GCTest.txt", FileMode.Append); StreamWriter sw = new StreamWriter(fs); try { sw.WriteLine(connStr); sw.Flush(); sw.Close(); fs.Close(); } catch (IOException e) { sw.Flush(); sw.Close(); fs.Close(); } } } }
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GCHandle { public class Person { int count = 0; public string Name { get; set; } public int Age { get; set; } public int Height { get; set; } public Person() { } public Person(string name, int age, int height) { this.Age = age; } //析构函数 ~Person() { count++; Program.write($"Person调用了析构函数{count}次"); //Cleaning up code goes here Console.WriteLine($"Person调用了析构函数{count}次"); } } public class Father : Person { int count = 0; public Father() { } public Father(string name, int age, int height) { this.Age = age; } //析构函数 ~Father() { count++; Program.write($"Father调用了析构函数{count}次"); //Cleaning up code goes here Console.WriteLine($"Father调用了析构函数{count}次"); } } public class Son : Father { int count = 0; public Son() { } public Son(string name, int age, int height) { this.Age = age; } //析构函数 ~Son() { count++; Program.write($"Son调用了析构函数{count}次"); //Cleaning up code goes here Console.WriteLine($"Son调用了析构函数{count}次"); } } }
输出结果:
32
可以发现当son=null,析构函数并没有执行,奇怪的是,程序退出后,应该会调用析构函数,但是,并没有在文件看到写入的值,这点我再。。。。。。思考思考。
如果一旦你使用完对象你就想调用析构函数,你该怎么做?有两个方法:1.启用GC.Collect,但在大多数情况下,这应该避免因为它会导致性能问题。2.实现IDisposable的Dispose方法,Dispose是用于释放所有资源对象,包括托管的和非托管的。
1.GC.Collect,输出结果:
32 Son调用了析构函数1次 Father调用了析构函数1次 Person调用了析构函数1次
并且在文件中也写入了调用析构函数的信息,子类调用析构函数,父类也会调用析构函数
Son调用了析构函数1次
Father调用了析构函数1次
Person调用了析构函数1次
首先,对于垃圾回收而言,在C#中,托管资源的垃圾回收是通过CLR的Garbage Collection来实现的,Garbage Collection会调用堆栈上对象的析构函数完成对象的释放工作;而对于一些非托管
资源,比如数据库链接对象等,需要实现IDisposable接口进行手动的垃圾回收。
2.实现IDisposable的Dispose方法,在.NET的对象中实际上有两个用于释放资源的函数:Dispose和Finalize,这里先介绍Dispose,Dispose是用于释放所有资源,包括托管的和非托管的。当我
们自定义的类及其业务逻辑中引用某些托管和非托管资源,就需要实现IDisposable接口,实现对这些资源对象的垃圾回收。
using System; using System.Collections.Generic; using System.Data.SqlClient; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GCHandle { public class Person : IDisposable { int count = 0; public string Name { get; set; } public int Age { get; set; } public int Height { get; set; } public Person() { Console.WriteLine($"Person构造函数"); } public Person(string name, int age, int height) { this.Age = age; } //析构函数 ~Person() { count++; Dispose(false);// 清空你调用的非托管对象,不是清空自己 Program.write($"Person调用了析构函数{count}次"); Console.WriteLine($"Person调用了析构函数{count}次"); } private bool _isDisposed = false; public void Dispose() {
// 在这里清空所有你调用的托管对象。 this.Dispose(true); // 该方法告诉垃圾回收器有一个类不再需要调用其析构函数了 // 因为Dispose()方法已经完成了所有需要清理的工作,所以析构函数不需要做任何工作 // 调用SuppressFinalize()方法就意味着垃圾会后期认为这个对象根本没有析构函数 System.GC.SuppressFinalize(this); } //另外,把Dispose(bool disposing)方法设置为protected virtual的原因是希望有子类可以一起参与到垃圾回收逻辑的设计,而且还不会影响到基类。 protected virtual void Dispose(bool disposing) { // 使用这种结构的原因是,当一个对象被释放时,它所占用的资源必须被全部释放 // 释放既可以通过代码实现,也可以通过DotNet垃圾回收机制来完成 // 因为垃圾回收机制只能释放托管的资源,因此非托管资源必须通过代码进行释放 // 首先需要判断该对象有没有被释放,在没有被释放的情况下才进行释放 if (!this._isDisposed) { // 判断是否执行托管资源的释放 // 垃圾回收机制认为每个类所占有的资源由类本身进行释放, // 如果一个类中包含另一个类对象的引用,该引用对象所占有的资源的释放由该引用对象自身来保证 // 因此,正常情况下都是在析构函数中调用 Dispose(false) if (disposing) { // 在这里清空所有你调用的托管对象。
Dispose(); Console.WriteLine("清空托管对象"); } else { // 清空你调用的非托管对象 Console.WriteLine("清空非托管对象"); }
_isDisposed = true; } } } }
using System; using System.Collections.Generic; using System.Data.SqlClient; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; namespace GCHandle { class Program { static void Main(string[] args) { test1(); Console.ReadKey(); } static void test1() { Person p = new Person(); p.Age = 32; Console.WriteLine(p.Age); p = null; } public static void write(string connStr) { FileStream fs = new FileStream("GCTest.txt", FileMode.Append); StreamWriter sw = new StreamWriter(fs); try { sw.WriteLine(connStr); sw.Flush(); sw.Close(); fs.Close(); } catch (IOException e) { sw.Flush(); sw.Close(); fs.Close(); } } } }
注意:
析构函数用于析构类的实例。
1) 不能在结构中定义析构函数。只能对类使用析构函数。
2) 一个类只能有一个析构函数。
3) 无法继承或重载析构函数。
4) 无法调用析构函数。它们是被自动调用的。
5) 析构函数既没有修饰符,也没有参数。
关于Finalize的介绍,可以看下这篇博文【C#】GC和析构函数(Finalize 方法)