C#实现IDisposable接口释放非托管资源

目录
  • 完整示例
  • 为什么要实现Foo析构函数
  • Dispose方法中为什么要调用GC.SuppressFinalize

Reference

Why using finalizers is a bad idea

当在一个类中使用了另外一个实现了IDisposable的类作为一个成员属性时, 此时这个类就有必要也去实现IDisposable接口, 以确保在合适的实际释放非托管资源, 到底该如何正确的实现这个接口呢? 当然这只是需要实现IDisposable接口其中一种情况

完整示例

示例的Foo类中包含了一个Stream类型的_stream成员, 因此需要为Foo类实现IDisposable模式

public class Foo : IDisposable
{
    private bool _disposed;
    private readonly Stream? _stream;
    public Foo()
    {
        _stream = File.Create("1.txt", 2048);
    }
    ~Foo()
    {
        CleanupUnmanagedResources();
    }
    private void CleanupUnmanagedResources()
    {
        if (_disposed) return;
        // 释放非托管资源
        _stream?.Dispose();
        _disposed = true;
    }
    public void Dispose()
    {
        CleanupUnmanagedResources();
        GC.SuppressFinalize(this);
    }
}

为什么要实现Foo析构函数

因为人性的弱点()

哈哈, 其实因为我们在使用Foo时可能会忘记手动调用其Dispose方法, 这个时候如果没有析构函数的话, 很可能导致资源永远得不到释放最终酿成内存泄漏的惨剧.

当然啦, 在析构函数中释放非托管资源可能会给GC带来额外的开销, 所以最好的做法是依然是使用using块保证能够及时的调用Dispose方法, 这里使用析构函数只是为了防止意外的发生. 至于为什么说在析构函数中释放非托管资源会导致额外的GC开销呢, 这涉及到GC回收过程,GC在处理包含析构函数的类时不会立即将此类回收, 而是会被GC标记为下一代, 这样这个被标记为下一代的类只有在GC决定回收下一代的垃圾对象时, 才会被真正回收掉, 这样一来就会导致额外的内存和性能开销了.

Dispose方法中为什么要调用GC.SuppressFinalize

GC.SuppressFinalize方法可以告诉GC不需要在调用此类的析构函数(Finalizers)了;

因为在Foo类的析构函数中调用了Foo.CleanupUnmanagedResources方法, 当GC回收此类调用此类析构函数时, 有可能会导致两次调用Foo.CleanupUnmanagedResources(第一次是Dispose方法中调用的)导致额外的开销,

所以当我们手动调用了Foo.Dispose(通过是通过using语法糖)后, 就需要告诉GC, "你回收我的时候用不着调用我的析构函数了, 该释放的资源我早就释放掉了已经", 转换成代码就是GC.SuppressFinalize

以上就是C#实现IDisposable接口释放非托管资源的详细内容,更多关于C# IDisposable接口释放资源的资料请关注我们其它相关文章!

(0)

相关推荐

  • C#中的IDisposable模式用法详解

    本文实例讲述了C#中IDisposable模式的用法,针对垃圾资源的回收进行了较为详细的讲解.分享给大家供大家参考之用.具体方法如下: 首先,对于垃圾回收而言,在C#中,托管资源的垃圾回收是通过CLR的Garbage Collection来实现的,Garbage Collection会调用堆栈上对象的析构函数完成对象的释放工作:而对于一些非托管资源,比如数据库链接对象等,需要实现IDisposable接口进行手动的垃圾回收.那么什么时候使用Idisposable接口,以及如何使用呢? 先来参考一

  • C#中IDispose接口的实现及为何这么实现详解

    前言 我原本认为对于IDispose的实现方法,只要在里面释放非托管资源就行了,但是通过网上资料,看到很多实现方法并不是仅仅做释放非托管资源,非常迷惑,关键是这些资料也没详细的告诉你为什么这么做?之后通过StackOverflow了解到这一步一步的原因,说的十分详细,结合自己的认识,翻译后分享给大家: 一.IDispose的实现方法 具体的实现方法,你可以直接查看这个我们网站的教程: //www.jb51.net/article/54899.htm 如果你能看懂,并且很清楚为什么那么做.那么以下

  • 详解C# 托管资源和非托管资源

    托管资源指的是.NET可以自动进行回收的资源,主要是指托管堆上分配的内存资源.托管资源的回收工作是不需要人工干预的,有.NET运行库在合适调用垃圾回收器进行回收. 非托管资源指的是.NET不知道如何回收的资源,最常见的一类非托管资源是包装操作系统资源的对象,例如文件,窗口,网络连接,数据库连接,画刷,图标等.这类资源,垃圾回收器在清理的时候会调用Object.Finalize()方法.默认情况下,方法是空的,对于非托管对象,需要在此方法中编写回收非托管资源的代码,以便垃圾回收器正确回收资源. 在

  • 详解c# 接口IDisposable的用法

    C#的每一个类型都代表一种资源,而资源又分为两类: 托管资源  由CLR管理分配和释放的资源,即从CLR里new出来的对象. 非托管资源  不受CLR管理的对象,如Windows内核对象,或者文件.数据库连接.套接字.COM对象等. 如果类型用到了非托管资源,或者需要显式释放托管资源,那么需要让类型继承接口IDisposable.记住:如果类型需要显式释放资源,那么一定要继承IDisposable接口.如: class SampleClass:IDisposable { private IntP

  • 基于C#中IDisposable与IEnumerable、IEnumerator的应用

    C#中如何合理的释放非托管内存?在本文中我们将讲解使用IDisposable释放托管内存和非托管内存. A.首先需要让类实现IDisposable接口,然后实现IDispose方法. A.a核心Disponse(bool isDisponse) 1.此方法首先判断isReadyDisposed(判断是否第一次调用此核心方法),如果不是第一次调用则不做任何操作. 2.再判断是否是析构函数调用?如果是析构函数调用不释放托管资源,其交由GC进行释放,如果析构函数释放托管资源可能之前GC释放过,就会导致

  • C#实现IDisposable接口释放非托管资源

    目录 完整示例 为什么要实现Foo析构函数 Dispose方法中为什么要调用GC.SuppressFinalize Reference Why using finalizers is a bad idea 当在一个类中使用了另外一个实现了IDisposable的类作为一个成员属性时, 此时这个类就有必要也去实现IDisposable接口, 以确保在合适的实际释放非托管资源, 到底该如何正确的实现这个接口呢? 当然这只是需要实现IDisposable接口其中一种情况 完整示例 示例的Foo类中包含

  • .net非托管资源的回收方法

    本文实例讲述了.net非托管资源的回收方法,分享给大家供大家参考.具体分析如下: 释放未托管的资源有两种方法   1.析构函数 2.实现System.IDisposable接口   一.析构函数  构造函数可以指定必须在创建类的实例时进行的某些操作,在垃圾收集器删除对象时,也可以调用析构函数.析构函数初看起来似乎是放置释放未托管资源.执行一般清理操作的代码的最佳地方.但是,事情并不是如此简单.由于垃圾回收器的运行规则决定了,不能在析构函数中放置需要在某一时刻运行的代码,如果对象占用了宝贵而重要的

  • 带你复习c# 托管和非托管资源

    前言 c# 托管和非托管比较重要,因为这涉及到资源的释放. 现在只要在计算机上运行的,无论玩出什么花来,整个什么概念,逃不过输入数据修改数据输出数据(计算机本质),这里面有个数据的输入,那么我们的内存有限啊,这里面就牵扯到数据释放. 看下c# 的垃圾回收是怎么样的. 了解垃圾回收之前首先要了解数据,了解数据需要了解数据类型啊,数据类型分为值类型还有引用类型. windows 使用一个虚拟寻址系统,该系统把程序可用的内存地址映射到硬件内存中的实际地址上,这些任务完全由windows 在后台管理.我

  • c#关于非托管内存的释放问题及解读

    目录 关于非托管内存的释放问题 托管内存与非托管内存之间的转换 1.managed memory-> unmanaged memory 2.un-managed memory->managed memory 3.在c#直接申请一个un-managed mem传给c++ 总结 关于非托管内存的释放问题 硬件:大华sdk 软件平台:win10+vs2015 背景:近期在做大华工业相机SDK的采集的时候,用到Marshal.copy,将托管的代码转换成非托管的指针内存,由于没有及时释放内存指针,导致

  • C#托管内存与非托管内存之间的转换的实例讲解

    c#有自己的内存回收机制,所以在c#中我们可以只new,不用关心怎样delete,c#使用gc来清理内存,这部分内存就是managed memory,大部分时候我们工作于c#环境中,都是在使用托管内存,然而c#毕竟运行在c++之上,有的时候,(比如可能我们需要引入一些第三方的c++或native代码的库,在Unity3d开发中很常见)我们需要直接在c#中操纵非托管的代码,这些non-managed memory我们就需要自己去处理他们的申请和释放了, c# 中提供了一些接口,完成托管和非托管之间

  • C#加载嵌入到资源的非托管dll

    如何加载非托管Dll 我们总会遇到需要加载非Win32的非托管dll,这里推荐一种方式就是将那些非win32的非托管dll嵌入资源的方式,在入口解压并且加载的方式,我先来看看如何实现吧,首先我们准备好demo,新增控制台项目如下: 代码如下: static void Main(string[] args) { UnzipAndLoad(); } /// <summary> /// 解压资源并且加载非托管DLL /// </summary> static void UnzipAndL

  • C#+无unsafe的非托管大数组示例详解(large unmanaged array in c# without ‘unsafe’ keyword)

    C#申请一个大数组(Use a large array in C#) 在C#里,有时候我需要能够申请一个很大的数组.使用之.然后立即释放其占用的内存. Sometimes I need to allocate a large array, use it and then release its memory space immediately. 由于在C#里提供的 int[] array = new int[1000000]; 这样的数组,其内存释放很难由程序员完全控制,在申请一个大数组后,程序

  • C#使用stackalloc分配堆栈内存和非托管类型详解

    目录 stackalloc 表达式 stackalloc 分配 System.Span<T> 或 System.ReadOnlySpan<T> 类型 stackalloc 分配 指针类型 stackalloc分配内存的注意点 非托管类型 Unmanaged type stackalloc 表达式 stackalloc表达式在栈(stack)上分配内存块. 在方法执行期间创建的栈中分配的内存块会在方法返回时自动丢弃.不能显式释放使用 stackalloc 分配的内存.stackall

  • C#(.Net)将非托管dll嵌入exe中的实现

    目录 托管dll与非托管dll 下载与安装 添加Dll 调用 编译 托管dll与非托管dll 托管dll实际上是指C#编写的dll,可以直接右键"引用"导入 而大部分情况下,我们需要引用C++写的dll,如果你的dll是使用 DllImport来导入的,那么它就属于非托管dll,这种dll无法直接嵌入exe中,需要借助工具:Costura.Fody,该工具可以使用VS直接下载 下载与安装 右键引用,选择"管理NuGet程序包",搜索 "fody"

随机推荐