C#托管类型与非托管类型
定义
托管对象指的是.net可以自动进行回收的资源,主要是指托管对象在堆上分配的内存资源。托管资源的回收工作是不需要人工干预的,有.net运行库在合适的时间进行回收。当然,也可以使用GC.Collect手动回收。
非托管对象指.net不知道如何回收的资源。例如文件、窗口、网络连接、数据库连接、画刷、图标等。这类资源,垃圾回收器在清理的时候会调用Object.Finalize()方法。默认情况下,方法是空的,对于非托管对象在此方法中需要编写回收非托管对象的代码,以便垃圾回收器正确回收。(例如我们通常打开文件、图片等后需要进行Close()或者Dispose()去释放)。
如果某个类型是以下类型之一,则它是非托管类型 :
sbyte
、byte
、short
、ushort
、int
、uint
、long
、ulong
、nint
、nuint
、char
、float
、double
、decimal
或bool
- 任何枚举类型
- 任何指针类型
- 成员都是某个非托管类型的一个元组
- 任何仅包含非托管类型字段的用户定义的结构类型。
可使用 unmanaged
约束指定:类型参数为“非指针、不可为 null 的非托管类型”。
仅包含非托管类型的字段的构造结构类型也是非托管类型,如以下示例所示:
using System;
public struct Coords<T>
{
public T X;
public T Y;
}
public class UnmanagedTypes
{
public static void Main()
{
DisplaySize<Coords<int>>();
DisplaySize<Coords<double>>();
}
private unsafe static void DisplaySize<T>() where T : unmanaged
{
Console.WriteLine($"{typeof(T)} is unmanaged and its size is {sizeof(T)} bytes");
}
}
// Output:
// Coords`1[System.Int32] is unmanaged and its size is 8 bytes
// Coords`1[System.Double] is unmanaged and its size is 16 bytes
非托管资源的释放
Microsoft为非托管资源的回收专门定义了一个接口:IDisposable,接口中只包含一个Dispose()方法。任何包含非托管资源的类,都应该继承此接口。
在一个包含非托管资源的类中,关于资源释放的标准做法是:
(1)继承IDisposable()接口;
(2)实现Dispose()方法,在其中释放托管资源和非托管资源,并将对象本身从垃圾回收器中移除(垃圾回收器不在回收此资源);
(3)实现类析构函数,在其中释放非托管资源。
但是这样之后,如果单纯依赖手动释放,仍然是不保险的,有时我们可能忘了手动释放资源,或者如果出现异常,仍可能导致析构函数不被执行,造成内存泄漏。
所以有两种比较保险的做法,一种是使用try - catch - finally语句,另一种是使用using。后者的底层实现正是基于前者,两者没有本质区别。可以说using是C#的一个语法糖。但是如果using语句中分配的变量的类型没有实现IDisposable接口,编译器将会抛出异常。
如下面两段代码:
SqlConnection myConnection = null;
using (myConnection = new SqlConnection(connectionString))
{
myConnection.Open();
}
和
try
{
myConnection = new SqlConnection(connectionString);
myConnection.Open();
}
finally
{
myConnection.Dispose();
}
本质上是一样的。
对于一些可能实现或未实现IDisposable接口的对象,或者无法确定是否应该用using语句包裹某个对象时,由于其不确定性我们可以使用as操作符进行安全的销毁
object obj = Factory.CreateResource();
//如果不确定obj是否实现了IDisposable接口,下面是安全的销毁方式
using (obj as IDisposable)
{
Console.WriteLine(obj.ToString());
}