本文将告诉大家在 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的报错机制吗?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助   Vue的5种处理Vue异常的方法 相信大家对Vue都不陌生.在使用Vue的时候也会遇到报错,也会使用浏览器的F12 来查看报错信息.但 ...

  2. 记录--可视化大屏-用threejs撸一个3d中国地图

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 不想看繁琐步骤的,可以直接去github下载项目,如果可以顺便来个star哈哈 本项目使用vue-cli创建,但不影响使用,主要绘制都已封 ...

  3. KingbaseES Json 系列十一:Json数组操作函数

    KingbaseES Json 系列十一--Json数组操作函数(JSONB_ARRAY_ELEMENTS,JSONB_ARRAY_ELEMENTS_TEXT,JSONB_ARRAY_LENGTH,J ...

  4. OGNL表达式注入分析

    OGNL基础 依赖 <dependency> <groupId>ognl</groupId> <artifactId>ognl</artifact ...

  5. 【已解决】ERROR: but there is no HDFS_NAMENODE_USER defined. Aborting operation. Starting datanodes

    export HDFS_NAMENODE_USER=rootexport HDFS_DATANODE_USER=rootexport HDFS_SECONDARYNAMENODE_USER=roote ...

  6. DM数据库金融行业案例(水贴一波)

      最近没遇到啥有意思的案例,都是些很简单的案例,但是又好久没写过博客了,决定水一波帖子,保持更新. 今天这个是任总老婆小王同学提供的金融SQL案例,难是不难,但是远程的时候网络卡得要命, 心累. 慢 ...

  7. Bootstrap实战 - 单页面网站

    一.介绍 单页面结构简单.布局清晰,常常用来做手机 App 或者某个产品的下载介绍页面.现在,展示型网页整体趋向于单页网站设计,这样一次性把核心信息展现出来,对于用户来说更加直观和简单,能够快速了解一 ...

  8. Python删除文件、文件夹----os

    使用 os  删除文件 import os '''删除文件 语法: os.unlink(path) 示例: 删除 b 文件夹中的 12.txt ''' os.unlink('b/12.txt')   ...

  9. K8S 性能优化-K8S Node 参数调优

    前言 K8S 性能优化系列文章,本文为第四篇:Kubernetes Node 性能优化参数最佳实践. 系列文章: <K8S 性能优化 - OS sysctl 调优> <K8S 性能优 ...

  10. 在HarmonyOS上实现ArkTS与H5的交互

      介绍 本篇Codelab主要介绍H5如何调用原生侧相关功能,并在回调中获取执行结果.以"获取通讯录"为示例分步讲解JSBridge桥接的实现. 相关概念 Web组件:提供具有网 ...