RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1602126
Accepted
Aycon
Aycon
Asked:2024-12-10 22:06:57 +0000 UTC2024-12-10 22:06:57 +0000 UTC 2024-12-10 22:06:57 +0000 UTC

工厂上的“using”语句将工厂委托给实例的资源释放

  • 772

这是一个最低限度可重现的示例:

namespace IterableDisposeTest
{
  interface IInstance
  {
    int ID { get; }
  }

  class Instance : IInstance
  {
    private static int p_counter = 0;

    public int ID { get; }

    public Instance(MagicVariable _magicVariable)
    {
      ID = p_counter++;
      Console.WriteLine(_magicVariable.MagicString);
    }
  }

  interface IProducer
  {
    public IEnumerable<IInstance> GetNextInstance();
  }
  
   
  class Producer : IProducer
  {
    private MagicVariable p_magicVariable;

    public Producer(MagicVariable _magicVariable)
    {
      p_magicVariable = _magicVariable;
    }

    public IEnumerable<IInstance> GetNextInstance()
    {
      for (int i = 0; i < 42; i++)
        yield return new Instance(p_magicVariable);
    }
  }

  class ProducerFactory: IDisposable
  {
    private bool disposedValue;
    private MagicVariable p_magicVariable = new MagicVariable();

    public IEnumerable<Producer> GetNextProducer()
    {
      while (true)
        yield return new Producer(p_magicVariable);
    }

    protected virtual void Dispose(bool disposing)
    {
      if (!disposedValue)
      {
        Console.WriteLine("ProducerFactory Disposed");
        if (disposing)
        {

        }

        p_magicVariable.Dispose();
        disposedValue = true;
      }
    }

    ~ProducerFactory()
    {
      Dispose(disposing: false);
    }

    public void Dispose()
    {
      Dispose(disposing: true);
      GC.SuppressFinalize(this);
    }
  }

  class MagicVariable: IDisposable
  {
    private string p_magicString = "I'm MAGIC!";
    private bool p_disposedValue;

    public string MagicString
    {
      get
      {
        ObjectDisposedException.ThrowIf(p_disposedValue, typeof(string));
        return p_magicString;
      }
    }

    protected virtual void Dispose(bool disposing)
    {
      if (!p_disposedValue)
      {
        Console.WriteLine("MagicVariable Disposed");
        if (disposing)
        {
        }

        p_disposedValue = true;
      }
    }

    ~MagicVariable()
    {
      Dispose(disposing: false);
    }

    public void Dispose()
    {
      Dispose(disposing: true);
      GC.SuppressFinalize(this);
    }
  }

  static class SuperProducer
  {
    public static IEnumerable<IProducer> GetNextProducer()
    {
      using ProducerFactory factory = new ProducerFactory();
      foreach(var producer in factory.GetNextProducer())
        yield return producer;
    }
  }

  internal class Program
  {
    static void Main(string[] args)
    {
      IProducer? producer = SuperProducer.GetNextProducer().FirstOrDefault((IProducer?)null);
      if (producer != null)
      {
        // Thrown ObjectDisposedException there:
        foreach (IInstance instance in producer.GetNextInstance())
          Console.WriteLine($"Instance id is: {instance.ID}");
      }
    }
  }
}

它会导致异常ObjectDisposedException,因为工厂在第 124 行退出语句时被释放。using困境如下 - 只有当语句中没有创建工厂时,错误才会消失using,但这与 RAII 概念相矛盾,工厂不会被创建。释放。如果你在 - 中创建一个工厂,那么它会在从第 126 行的循环using返回之前被释放。请告诉我这个示例中的架构错误是什么?IProducer你能怎样解决它?

c#
  • 1 1 个回答
  • 53 Views

1 个回答

  • Voted
  1. Best Answer
    Pavel Mayorov
    2024-12-11T00:51:14Z2024-12-11T00:51:14Z

    你实际上期望什么?你在工厂(ProducerFactory)和实例(Instance)之间共享一个资源(MagicVariable),而资源的生命周期由工厂控制,工厂在实例之前销毁。没有必要这样做。

    每个资源都有一个范围和生命周期,好的代码应该与这些相匹配。换句话说,您无法将资源转移到其链接比资源本身寿命更长的位置。是的,在有垃圾收集的语言中,这条规则不像没有垃圾收集的语言那么严格,但损坏的程序仍然是损坏的程序,尽管是安全损坏的程序。

    让我们看一个简单的例子:

    Resource res;
    
    {
        using var tmp = new Resource();
        res = tmp;
    }
    
    res.Access(); // вот тут возникнет исключение
    

    看起来很明显你不应该这样写?那么为什么您认为通过将 tmp 变量包装在 ProducerFactory 中并将 res 变量包装在 Instance 和 Producer 中可以避免此异常?


    那么,工厂和资源该怎么办呢?而且只有三个选择:

    1. 如果工厂拥有由所有生成的对象共享的资源,则意味着当这些相同的对象处于活动状态时无法释放该工厂。

      你的超级制片人打破了这条规则。工厂必须在更高的级别上创建:

      static class SuperProducer
      {
          public static IEnumerable<IProducer> GetNextProducer(ProducerFactory factory)
          {
              foreach(var producer in factory.GetNextProducer())
                  yield return producer;
          }
      }        
      internal class Program
      {
          static void Main(string[] args)
          {
              using ProducerFactory factory = new ProducerFactory();
              IProducer? producer = SuperProducer.GetNextProducer(factory ).FirstOrDefault((IProducer?)null);
              if (producer != null)
              {
                  foreach (IInstance instance in producer.GetNextInstance())
                      Console.WriteLine($"Instance id is: {instance.ID}");
              }
          }
      }
      
    2. 如果这个选项完全不能令人满意,则意味着工厂不应该拥有该资源。为每个生成的对象创建单独的资源。

    3. 如果这个选项又不合适,并且资源不得不被共享,那么就该计算链接了。计算有多少个对象拥有某个资源,只有当该数量为零时才释放它。

    • 3

相关问题

  • 使用嵌套类导出 xml 文件

  • 分层数据模板 [WPF]

  • 如何在 WPF 中为 ListView 手动创建列?

  • 在 2D 空间中,Collider 2D 挂在玩家身上,它对敌人的重量相同,我需要它这样当它们碰撞时,它们不会飞向不同的方向。统一

  • 如何在 c# 中使用 python 神经网络来创建语音合成?

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5