具有 IDisposable 的无限状态机

2024-01-19

假设我有一个无限状态机来生成随机 md5 哈希值:

public static IEnumerable<string> GetHashes()
{
    using (var hash = System.Security.Cryptography.MD5.Create())
    {
        while (true)
            yield return hash.ComputeHash(Guid.NewGuid().ToByteArray());
    }
}

在上面的例子中我使用了using陈述。会不会.Dispose()方法曾经被调用过吗? CQ,非托管资源会被释放吗?

例如,如果我按如下方式使用机器:

public static void Test()
{
    int counter = 0;
    var hashes = GetHashes();
    foreach(var md5 in hashes)
    {
        Console.WriteLine(md5);
        counter++;
        if (counter > 10)
            break;
    }
}

自从hashes变量将超出范围(并且我假设垃圾收集)是否会调用 dispose 方法来释放所使用的资源System.Security.Cryptography.MD5或者这是内存泄漏?


让我们稍微更改一下原始代码块,将其归结为要点,同时仍然保持其足够有趣以进行分析。这并不完全等同于您发布的内容,但我们仍在使用迭代器的值。

class Disposable : IDisposable {
    public void Dispose() {
        Console.WriteLine("Disposed!");
    }
}

IEnumerable<int> CreateEnumerable() {
    int i = 0;
    using (var d = new Disposable()) {
       while (true) yield return ++i;
    }
}

void UseEnumerable() {
    foreach (int i in CreateEnumerable()) {
        Console.WriteLine(i);
        if (i == 10) break;
    }
}

这将在打印之前打印从 1 到 10 的数字Disposed!

幕后到底发生了什么?还有很多。我们先处理外层,UseEnumerable. The foreach是以下内容的语法糖:

var e = CreateEnumerable().GetEnumerator();
try {
    while (e.MoveNext()) {
        int i = e.Current;
        Console.WriteLine(i);
        if (i == 10) break;
    }
} finally {
    e.Dispose();
}

对于确切的细节(因为即使这也被简化了一点)我建议你C# 语言规范 https://www.microsoft.com/download/details.aspx?id=7029,第 8.8.4 节。这里重要的一点是foreach需要隐式调用Dispose的枚举器。

接下来,using中的声明CreateEnumerable也是语法糖。事实上,让我们用原始语句写出整个内容,以便稍后我们可以更理解翻译:

IEnumerable<int> CreateEnumerable() {
    int i = 0;
    Disposable d = new Disposable();
    try {
       repeat: 
       i = i + 1;
       yield return i;
       goto repeat;
    } finally {
       d.Dispose();
    }
}

语言规范第 10.14 节详细介绍了迭代器块实现的确切规则。它们以抽象操作而非代码的形式给出。关于 C# 编译器生成什么类型​​的代码以及每个部分的作用的很好的讨论在C# 深入探讨 http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx,但我将提供一个仍然符合规范的简单翻译。重申一下,这不是编译器会做的actually产生,但它是一个足够好的近似值来说明正在发生的事情,并且忽略了处理线程和优化的更多毛茸茸的部分。

class CreateEnumerable_Enumerator : IEnumerator<int> {
    // local variables are promoted to instance fields
    private int i;
    private Disposable d;

    // implementation of Current
    private int current;
    public int Current => current;
    object IEnumerator.Current => current;

    // State machine
    enum State { Before, Running, Suspended, After };
    private State state = State.Before;

    // Section 10.14.4.1
    public bool MoveNext() {
        switch (state) {
            case State.Before: {
                    state = State.Running;
                    // begin iterator block
                    i = 0;
                    d = new Disposable();
                    i = i + 1;
                    // yield return occurs here
                    current = i;
                    state = State.Suspended;
                    return true;
                }
            case State.Running: return false; // can't happen
            case State.Suspended: {
                    state = State.Running;
                    // goto repeat
                    i = i + 1;
                    // yield return occurs here
                    current = i;
                    state = State.Suspended;
                    return true;
                }
            case State.After: return false; 
            default: return false;  // can't happen
        }
    }

    // Section 10.14.4.3
    public void Dispose() {
        switch (state) {
            case State.Before: state = State.After; break;
            case State.Running: break; // unspecified
            case State.Suspended: {
                    state = State.Running;
                    // finally occurs here
                    d.Dispose();
                    state = State.After;
                }
                break;
            case State.After: return;
            default: return;    // can't happen
        }
    }

    public void Reset() { throw new NotImplementedException(); }
}

class CreateEnumerable_Enumerable : IEnumerable<int> {
  public IEnumerator<int> GetEnumerator() {
    return new CreateEnumerable_Enumerator();
  }

  IEnumerator IEnumerable.GetEnumerator() {
    return GetEnumerator();
  }
}

IEnumerable<int> CreateEnumerable() {
  return new CreateEnumerable_Enumerable();
}

这里的要点是代码块在出现时被分割yield return or yield break语句,迭代器负责记住中断时“我们在哪里”。任何finally体内的块被推迟到Dispose。代码中的无限循环实际上不再是无限循环,因为它被周期性中断yield return声明。注意,because the finally块实际上不是finally不再阻塞,当你处理迭代器时,它的执行就不太确定了。这就是为什么使用foreach(或任何其他确保Dispose迭代器的方法在 a 中被调用finally块)是必不可少的。

这是一个简化的例子;当你使循环变得更加复杂、引入异常等等时,事情会变得更加有趣。 “只是让这个工作”的负担落在编译器身上。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

具有 IDisposable 的无限状态机 的相关文章

随机推荐

  • 如何在 Razor 页面中获取 ASP.NET 身份验证票证过期?

    我将 Identityserver4 与 ASP NET Identity 结合使用 并使用配置为 SlidingExpiration true 和 ExpireTimeSpan 20 分钟的 cookie 我想在用户即将超时时向他们提供警
  • IIS Express 7.5 wwwroot 目录在哪里

    使用 Windows 7 我已经安装了 IIS Express 7 5 但我找不到wwwroot我系统上的目录 没有 inetpub我的 C 盘上也有 文件夹 您能否让我知道在哪里可以放置要运行的文件 例如 hello aspx 文件 Th
  • 如何检索当前为微调器设置的阵列适配器?安卓操作系统、机器人

    希望创建一个菜单切换器 在旋转器的两个可能的数组之间切换 例如 如果微调器设置为显示数组 A 那么当我按下此菜单按钮时 我希望将微调器设置为数组 B 如果我再次按下它 我希望将其设置回数组 A 我可以处理 if then 语句等 但如何调用
  • 子网格缓存或阻止子网格数据被删除 (jqGrid)

    有没有办法强制 jqGrid 在切换行时不删除子网格数据 我当前加载数据网格的过程如下 1 设置主网格 使用稍后可以使用的 ID 以编程方式设置子网格 2 通过json将数据本地加载到主网格中 3 通过json将数据添加到本地子网格 由于当
  • Wicket 和 CSS 资源

    我环顾四周 找不到将我自己的 CSS 添加到 Wicket 网站项目的傻瓜指南 但在开始之前 我对正确的 java 开发还很陌生 所以当我说 傻瓜指南 时 我是认真的 非常感谢这里对我简单明了的解释 我从这里开始阅读本指南 http wic
  • Spring Integration - 变压器和标头丰富器

    我的情况是这样的 我需要根据邮政编码将消息路由到三个不同的商店 为此 我需要查看邮件标头以查找客户的邮政编码 并执行以下计算 if zip lt 5000 store SJ else if zip gt 6000 store JY else
  • 为什么 Clojure 的多方法比“if”或“case”语句更好

    我花了一些时间试图理解 Clojure 多方法 据我了解 主要的 专业 多方法论点是它们的灵活性 但是 我对为什么多方法比简单的 if 或 case 语句更好的争论感到困惑 请有人解释一下 多态性和过度美化的案例陈述之间的界限在哪里 编辑
  • 了解 Cassandra 的存储开销

    我一直在阅读本节 http www datastax com documentation cassandra 2 0 cassandra architecture architecturePlanningUserData t html查阅
  • 如何以编程方式设置layout_margin? [复制]

    这个问题在这里已经有答案了 我想知道如何使用屏幕高度和宽度以编程方式设置layout marginLeft layout marginTop layout marginBottom 请帮我 谢谢 莫纳利 这是一个例子 改编自这个答案 htt
  • iOS 应用程序在 iOS 12 上编译器生成的代码中崩溃

    我最近发布了一个用 Swift 4 2 编写的 iOS 应用程序的新版本 Crashlytics 报告该应用程序在编译生成的代码中崩溃了 30 多次 我查看了项目中的类 并尝试重现崩溃 但未成功 有谁有办法解决编译器生成的代码中发生的崩溃吗
  • ASP .NET Core 2.0 将“localhost”更改为“主机名”

    我有一个基于MVC框架编写的Web应用程序 它在本地主机和默认端口 51290 上运行得非常好 现在我需要使用我的域名运行它 例如我的主机名 我尝试的是在 applicationhost config 部分添加一行
  • 从后台返回时 AVCaptureSession 失败

    我有一个相机预览窗口 90 的时间都运行良好 然而 有时 当返回我的应用程序时 如果它位于后台 预览将不会显示 这是我在视图加载时调用的代码 void startCamera session AVCaptureSession alloc i
  • SearchView getActionView 返回 null

    前几天还可以用 但是突然就停止了 我只想在某些片段可见时使用操作栏搜索小部件 现在我无法获得SearchView now getActionView总是返回 null 可搜索 xml
  • 使用 php 和 mysql 发送提醒电子邮件而不使用 cron-job?

    我刚刚制作了一个 php 脚本 它将在约会开始前 2 天向网站管理员发送电子邮件提醒 我本来打算自动化脚本来运行 cron 作业 却发现我托管的人 疯狂的域 似乎没有 Cron 作业 有没有办法在没有 cron jobs 的情况下做到这一点
  • 转置没有聚合的行和列

    我有以下数据集 Account Contact 1 324324324 1 674323234 2 833343432 2 433243443 3 787655455 4 754327545 4 455435435 5 543544355
  • 如何在此 Builder 实现中摆脱 instanceof

    The idea 我需要创建命令 命令可以配置参数 并非每个命令都可以接收相同的参数 所以有些必须被忽略 我有一个抽象类 Command 其中定义了一个 Builder 默认情况下 每个附加参数都会抛出 UnsupportedOperati
  • 在 pandas 中使用带有元组列的查询

    我有一个 pandas df 其中一列作为元组 我想用query使用元组的第一个条目对 df 进行子集化 最好的方法是什么 我在 pandas 23 3 Python 3 6 6 MWE import pandas as pd df pd
  • Gitlab-ci.yml 创建合并请求

    我在 DEV 分支中运行以下 gitlab ci yml 文件 目标也为 DEV 由于我无法将 TARGET 指向 MASTER 因此不会自动创建 MR 我想知道是否可以在 gitlab ci 脚本本身中创建合并请求 dev stage d
  • 拉力赛中的速度图[关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 我正在开展一个项目 从拉力赛中提取数据并创建速度图表 我了解要使用的 REST Web 服务 API 是缺陷 迭代 分层需求和迭代累积
  • 具有 IDisposable 的无限状态机

    假设我有一个无限状态机来生成随机 md5 哈希值 public static IEnumerable