本文将告诉大家在 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. 三维模型3DTile格式轻量化顶点压缩主要技术方法分析

    三维模型3DTile格式轻量化顶点压缩主要技术方法分析 三维模型顶点压缩是3DTile格式轻量化压缩的重要组成部分,能有效减小数据大小,提高数据处理效率.下面将详细分析几种主要的顶点压缩技术方法: 预 ...

  2. Python配置文件使用教程

    在 Python 应用程序开发过程中,配置文件扮演着重要的角色.配置文件可以用来存储应用程序的各种设置.选项和参数,使得程序更加灵活和可配置.本文将介绍 Python 中如何使用配置文件,并提供一些常 ...

  3. vue要做权限管理该怎么做?如果控制到按钮级别的权限怎么做?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.是什么 权限是对特定资源的访问许可,所谓权限控制,也就是确保用户只能访问到被分配的资源 而前端权限归根结底是请求的发起权,请求的发起可 ...

  4. Liunx-LVM创建与扩容

    LVM是 Logical Volume Manager(逻辑卷管理)的简写,它是Linux环境下对磁盘分区进行管理的一种机制,它由Heinz Mauelshagen在Linux 2.4内核上实现,最新 ...

  5. PostgreSQL与Java JDBC数据类型对照 源码

    文件:postgresql-42.2.12.jar 类名:org.postgresql.jdbc.TypeInfoCache // basic pg types info: // 0 - type n ...

  6. 嵌入式C语言设计学习之C语言回顾

    C的基本语法-回忆 1.C的结构 C语言的结构还是以函数为主体,通过其他资源的添加来实现高级语言逻辑.所有的操作都是基于主函数展开的.以主函数为顺序列表,其他函数作为功能模块,组成一个完整的系统.所以 ...

  7. KingbaseES 最老事务阻止vacuum freeze

    前言 最近生产环境发生几次由于长事务导致表.库年龄没法回收的情况.我们要规避这种情况的发生,不要等发生了再去强制中断会话连接.当数据库中存在最老事务版本xmin,那么早于他的快照可以被标记为froze ...

  8. SQL优化篇之-如何减少耗时查询的调用次数

    函数调用次数与性能 在查询语句中,如果 Select 子句调用了较为耗时的函数或子查询,需要特别考虑函数调用次数对于SQL整体执行时间的影响. 一.数据准备,SQL 语句 模拟较耗时的用户函数 确保执 ...

  9. XML 简介及用法详解

    XML 是一种用于存储和传输数据的与软件和硬件无关的工具. 什么是XML? XML代表eXtensible Markup Language(可扩展标记语言).XML是一种与HTML非常相似的标记语言. ...

  10. MyBatis resultMap中collection过滤空字段

    在使用MyBatis查询数据时,返回值可以定义为resultMap. 如果返回的对象中有列表,还可以使用collection标签进行定义. 此时,如果不想某些字段为空的数据加入列表,可以使用notNu ...