专栏首页林德熙的博客dotnet 6 使用 DependentHandle 关联对象生命周期

dotnet 6 使用 DependentHandle 关联对象生命周期

本文将告诉大家在 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 文件夹

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.lindexi.com/复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • dotnet ConditionalWeakTable 的底层原理

    在 dotnet 中有一个特殊的类,这个类能够做到附加属性一样的功能。也就是给某个对象附加一个属性,当这个对象被回收的时候,自然解除附加的属性的对象的引用。本文...

    林德熙
  • dotNET Core 3.X 依赖注入

    如果说在之前的 dotNET 版本中,依赖注入还是个比较新鲜的东西,那么在 dotNET Core 中已经是随处可见了,可以说整个 dotNET Core 的框...

    oec2003
  • 浅入 .NET Core 中的内存和GC知识

    【1】https://docs.microsoft.com/zh-cn/dotnet/standard/managed-code

    痴者工良
  • .NET 云原生架构师训练营(模块二 基础巩固 依赖注入)--学习笔记

    ASP.NET Core 依赖注入:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/depe...

    郑子铭
  • .NET 云原生架构师训练营(模块二 基础巩固 依赖注入)--学习笔记

    ASP.NET Core 依赖注入:https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/depe...

    郑子铭
  • ASP.NET Core 6 的性能改进

    受到 由Stephen Toub 发布的关于 .NET 性能的博客的启发,我们正在写一篇类似的文章来强调ASP.NET Core 在6.0 中所做的性能改进。

    郑子铭
  • [Cake] 1. CI中的Cake

    在上一篇C#Make自动化构建-简介中,简单的介绍了下Cake的脚本如何编写以及通过Powershell在本地运行Cake脚本。本篇在此基础上,介绍下如何在CI...

    blackheart
  • 【译】ASP.NET Core 6 中的性能改进

    受到 Stephen Toub 关于 .NET 性能的博文的启发,我们正在写一篇类似的文章来强调 6.0 中对 ASP.NET Core 所做的性能改进。

    郑子铭
  • .NET Core 跨平台执行命令、脚本

    晓晨
  • 【译】ASP.NET Core 6 中的性能改进

    受到 Stephen Toub 关于 .NET 性能的博文的启发,我们正在写一篇类似的文章来强调 6.0 中对 ASP.NET Core 所做的性能改进。

    郑子铭
  • Entity Framework Core 2.0 入门

    LearnEf.Console依赖LearnEf.Domains和LearnEf.Data:

    solenovex
  • CA1710:标识符应具有正确的后缀

    按照约定,扩展某些基类型或实现某些接口的类型的名称,或者由这些类型派生的类型的名称应具有与相应基类型或接口关联的后缀。

    呆呆
  • ASP.Net Core 开发笔记

    此处以一个Web API 项目为例, 针对不太大的项目,采用了一个划分为三层的结构。

    李郑
  • Entity Framework Core 2.0 入门

    该文章比较基础, 不多说废话了, 直接切入正题. 该文分以下几点: 创建Model和数据库 使用Model与数据库交互 查询和保存关联数据 EF Core支持情...

    solenovex
  • 创建指标

    本文适用范围:✔️ .NET Core 3.1 及更高版本 ✔️ .NET Framework 4.6.1 及更高版本

    呆呆
  • 微软热门开源项目及代码库地址

    非常流行的跨平台代码编辑器,提供全面的编辑和调试支持、可扩展性模型以及与现有工具的轻量级集成。

    Edi Wang
  • 【壹个小技巧】一看就会的CI/CD :Github Actions

    当然,我们这里不说平时公司的正规方案,肯定是一大推的集成方案。咱们就说一下平时自己是如何做的:

    老张的哲学

扫码关注腾讯云开发者

领取腾讯云代金券