本文将告诉大家在 dotnet 6 新加入的 System.Runtime.DependentHandle 的类型的使用方法,通过 DependentHandle 可以实现将某个对象的引用生命周期和另一个对象关联起来

如 DependentHandle 结构体的构造函数,要传入两个对象作为参数,这两个对象参数分别是 target 和 dependent 参数对象,表示的意义是将这两个对象通过 DependentHandle 结构体建立关联。让 target 对象关联上 dependent 对象的生命周期,在 dependent 对象没有被释放之前,不会先释放 target 对象。功能上和 ConditionalWeakTable 差不多,只是 DependentHandle 作为 ConditionalWeakTable 的底层实现支持,直接使用 DependentHandle 可以有更多的控制

原本咱可以使用 ConditionalWeakTable 将对象进行关联,实现到将某个对象关联到另一个对象的生命周期上,只要另一个对象没有被释放,那么关联的对象也不会被释放。如此可以在不改动原有代码的前提下,让两个毫不关联的对象进行关联。例如可以用来实现缓存模块的功能

然而 ConditionalWeakTable 算是一个上层的封装,如果想要做更多的定制功能,那就可以采用在 dotnet 6 新加入的 System.Runtime.DependentHandle 结构体。这个结构体提供的是一对一的关联关系,而且可以方便调用 Dispose 方法解除关联性

接下来将写一个例子来告诉大家 DependentHandle 类型的使用方法

新建一个 WPF 项目,在界面放三个按钮,分别是进入断点按钮,用于进入断点看对象是否被释放。释放引用按钮,将关联的对象释放引用。由于在 .NET 里面,释放引用不代表立刻被回收,再加上一个 GC 按钮,用来点击的时候触发 GC 逻辑

        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Orientation="Horizontal">
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Margin" Value="10,10,10,10" />
</Style>
</StackPanel.Resources>
<Button Click="Button_Click">
进入断点
</Button>
<Button x:Name="FreeObjectButton" Click="FreeObjectButton_Click">
释放引用
</Button>
<Button x:Name="GCButton" Click="GCButton_Click">
GC
</Button>
</StackPanel>

在后台代码里面定义 Foo1 和 Foo2 两个类型,定义类型的用途是在于方便在内存里面找到这两个类型的对象

public class Foo1
{ } public class Foo2
{ }

这两个类型没有相互关联性,在 MainWindow 添加一个 Foo1 类型的字段,如此即可让 Foo1 类型的对象被 MainWindow 引用,不会被释放

    public MainWindow()
{
InitializeComponent(); Loaded += MainWindow_Loaded;
} private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var foo1 = new Foo1(); _foo1 = foo1;
} private Foo1? _foo1;

如果在 MainWindow_Loaded 方法里面,创建一个 Foo2 对象,而让这个对象没有地方引用,那自然很快就会 Foo2 对象就会被释放

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var foo1 = new Foo1(); new Foo2(); _foo1 = foo1;
}

如果将 Foo2 通过 DependentHandle 实现和 Foo1 关联,加入到关联列表里面,如以下逻辑,将可以让 Foo2 不会被释放

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
var foo1 = new Foo1(); HandleList.Add(new DependentHandle(foo1, new Foo2())); _foo1 = foo1;
} private List<DependentHandle> HandleList { get; } = new List<DependentHandle>();

假如 DependentHandle 是另外的某些类型,对 Foo2 的对象也有强引用关系,那自然 Foo2 不会被释放,只是不会被释放的理由是被 HandleList 给引用。但本文告诉大家的 DependentHandle 是不会对 Foo2 有强引用的,只是对 Foo2 关联 Foo1 对象

实现释放引用按钮的功能,点击按钮,将 Foo1 的对象引用释放,如此即可让 Foo1 的对象可以被回收

    private void FreeObjectButton_Click(object sender, RoutedEventArgs e)
{
_foo1 = null;
}

由于在 .NET 里面,不会立刻回收,实现 GC 按钮的功能,手动触发回收

    private void GCButton_Click(object sender, RoutedEventArgs e)
{
GC.Collect();
GC.WaitForFullGCComplete();
}

实现断点按钮的逻辑

    private void Button_Click(object sender, RoutedEventArgs e)
{
Debugger.Break();
}

运行代码,先点击一下释放按钮,让 _foo 释放引用,接着点击 GC 按钮触发回收逻辑。多次点击 GC 按钮,等待一下,然后点击进入断电按钮,自己打上断点,通过 VS 的内存调试,可以看到 Foo1 和 Foo2 对象都没有存在内存里面

同时在 HandleList 列表里面,可以看到有一个 DependentHandle 对象,但是此对象里面的 Target 和 Dependent 都是 null 空对象

这就是证明了,通过 DependentHandle 可以建立从 Target 到 Dependent 的引用关联,在 Dependent 被回收之前,不会回收 Target 对象。在 Dependent 被回收之后,才会回收 Target 对象

在经过测试,使用 DependentHandle 的回收速度是比较慢的,很多时候,不是第一次点击 GC 按钮进行回收就能回收掉 Foo1 和 Foo2 对象的,而是需要多次点击

使用 dotnet 6 加入的 DependentHandle 进行底层的控制,更好写出符合自己业务逻辑的对象关联逻辑。例如实现自己的缓存库等。这个 Dependent 的功能是需要 CLR 层面提供的,也就是说这个类型只能在 dotnet 6 和更高版本使用,详细请看 dotnet ConditionalWeakTable 的底层原理

更多请看 DependentHandle Struct (System.Runtime) Microsoft Docs

以上的代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 8378751cc0766bc804fa8a1ae5ef3e0766dd5b99

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 8378751cc0766bc804fa8a1ae5ef3e0766dd5b99

获取代码之后,进入 LemjallbabelyeeburKemkubejer 文件夹

dotnet 6 使用 DependentHandle 关联对象生命周期的更多相关文章

  1. Hibernate的三种状态及对象生命周期

        理解Hibernate的三种状态,更利于理解Hibernate的运行机制,这些可以让你在开发中对疑点问题的定位产生关键性的帮助. 三种状态 临时状态(Transient):在通过new关键字, ...

  2. .Net组件程序设计之对象生命周期

    .Net组件程序设计之对象生命周期 .NET 垃圾回收 IDisposable() Using语句 .NET 垃圾回收 是CLR管理着垃圾回收器,垃圾回收器监控着托管堆,而我们使用的对象以及系统启动是 ...

  3. Ninject之旅之三:Ninject对象生命周期

    摘要 DI容器的一个责任是管理他创建的对象的生命周期.他应该决定什么时候创建一个给定类型的对象,什么时候使用已经存在的对象.他还需要在对象不需要的时候处理对象.Ninject在不同的情况下管理对象的生 ...

  4. iOS视图控制对象生命周期

    iOS视图控制对象生命周期-init.viewDidLoad.viewWillAppear.viewDidAppear.viewWillDisappear.viewDidDisappear的区别及用途 ...

  5. IOS 视图控制对象生命周期-init、viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear等的区别及用途

    iOS视图控制对象生命周期-init.viewDidLoad.viewWillAppear.viewDidAppear.viewWillDisappear.viewDidDisappear的区别及用途 ...

  6. 【转】【iOS知识学习】_视图控制对象生命周期-init、viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear等的区别及用途

    原文网址:http://blog.csdn.net/weasleyqi/article/details/8090373 iOS视图控制对象生命周期-init.viewDidLoad.viewWillA ...

  7. _视图控制对象生命周期-init、viewDidLoad、viewWillAppear、viewDidAppear、viewWillDisappear等的区别及用途

    iOS视图控制对象生命周期-init.viewDidLoad.viewWillAppear.viewDidAppear.viewWillDisappear.viewDidDisappear的区别及用途 ...

  8. ASP.NET Core Web API下事件驱动型架构的实现(二):事件处理器中对象生命周期的管理

    在上文中,我介绍了事件驱动型架构的一种简单的实现,并演示了一个完整的事件派发.订阅和处理的流程.这种实现太简单了,百十行代码就展示了一个基本工作原理.然而,要将这样的解决方案运用到实际生产环境,还有很 ...

  9. Python学习手册之内部方法、操作符重载和对象生命周期

    在上一篇文章中,我们介绍了 Python 的类和继承,现在我们介绍 Python 的内部方法.操作符重载和对象生命周期. 查看上一篇文章请点击:https://www.cnblogs.com/dust ...

  10. Servlet对象生命周期(四)

    一.Servlet对象生命周期 一下图片说明上图第7点 destroy()方法是在停止tomcat服务器时执行 https://pan.baidu.com/s/1mgTabWW#list/path=% ...

随机推荐

  1. 你是怎么处理vue项目中的错误的?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.错误类型 任何一个框架,对于错误的处理都是一种必备的能力 在Vue 中,则是定义了一套对应的错误处理规则给到使用者,且在源代码级别,对 ...

  2. dotNet符号文件(pdb),符号包(snupkg)和SourceLink

    前言 本文的主题是 Visual Studio 调试 NuGet 包,以及符号包的概念,如何去发布一个 NuGet 包,让我们的 NuGet 包支持 SouceLink,这些都是我们开发中比较容易忽视 ...

  3. rabbitmq学习记录

    一.RabbitMQ的概念 RabbitMQ 是一个消息中间件:它接受并转发消息.你可以把它当做一个快递站点,当你要发送一个包裹时,你把你的包裹放到快递站,快递员最终会把你的快递送到收件人那里,按照这 ...

  4. 金仓数据库kbcrypto 插件实现sm加密算法

    首先介绍一下sm4 算法 SM4 算法是对称加密算法,国标 GB/T 32907 对 SM4 对称加密算法进行了详细描述.SM4 算法密钥长度固定为128bit,加密解密采用相同的密钥,加解密速度较快 ...

  5. KingabseES kingbase_fdw 跨库关联查询

    背景 我们在做综合应用项目的时候,通常会面临客户的每个应用系统使用各自的数据库,或者存放在不同的服务器.查询报表可能使用多个应用数据,这样就需要跨库读取数据表或视图. KINGBASE_FDW 是一种 ...

  6. 论文阅读小结(B/S和C/S结构)

    论文阅读小结 一.B/S 和 C/S 软件体系结构选择 1) C/S . B/S 结构概述 C/S 结构,即 Client/Server (客户机 / 服务器 ), C/S 结构软件分为客户机和服务器 ...

  7. 学习Source Generators之从swagger中生成类

    前面学习了一些Source Generators的基础只是,接下来就来实践一下,用这个来生成我们所需要的代码. 本文将通过读取swagger.json的内容,解析并生成对应的请求响应类的代码. 创建项 ...

  8. 微服务集成Spring Cloud Alibaba Seata (二) 客户端连接

    通过上篇文章后我们的Seata服务就部署成功了,如果还不清楚怎么部署或者还没有部署Seata服务的朋友可以看我写的上篇文章进行服务部署.Seata部署步骤:https://www.cnblogs.co ...

  9. #分块,可撤销并查集#洛谷 3247 [HNOI2016]最小公倍数

    题目 分析 考虑将询问和边权按 \(a\) 分别从小到大排序,考虑最暴力的做法就是将不超过 \(a'\) 且 不超过 \(b'\) 的边抽取出来 放进并查集判断 \(a,b\) 的最大值都能达到 \( ...

  10. #2-SAT,Tarjan,前缀优化建边#洛谷 6378 [PA2010]Riddle

    题目 \(n\) 个点 \(m\) 条边的无向图被分成 \(k\) 个部分.每个部分包含一些点. 请选择一些关键点,使得每个部分恰有一个关键点,且每条边至少有一个端点是关键点. 分析 每条边至少有一个 ...