RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 902842
Accepted
Kir_Antipov
Kir_Antipov
Asked:2020-11-06 16:31:44 +0000 UTC2020-11-06 16:31:44 +0000 UTC 2020-11-06 16:31:44 +0000 UTC

创建对象存储

  • 772

同志们,我遇到了以下问题:

我需要实现一个对象存储之类的东西,它会为每个用户提供一个指定对象的实例

我将尝试用伪代码来解释:

// Добавим в хранилище правило создания объекта 
storage.Add("tmp", () => new Foo()); 

{
    // В хранилище пока нет сгенерированных объектов
    // Так что создаётся новый, сохраняется и возвращается 
    Foo tmp0 = storage["tmp"];
}

// Здесь ссылка на tmp0 уже недействительна 
// Так что единственная ссылка на тот созданный объект лежит внутри хранилища 
// Возвращаем тот же объект, что был в tmp0
Foo tmp1 = storage["tmp"];

// Тот объект, что был создан для tmp0, а теперь хранится в tmp1, уже занят 
// Так что создаём новый объект, сохраняем его в хранилище и его же и возвращаем 
Foo tmp2 = storage["tmp"];

也就是说,当tmp0包含对同一对象的引用的存储范围结束时,它不会将其删除,而是将其保存到下一次对类似对象的请求为止。

所以逻辑是这样的:

  1. 按键请求对象
  2. 如果存储中没有已经创建的对象,则根据给定的规则生成,保存然后返回。否则:
  3. 检查每个创建的对象是否在用户代码中被引用。如果不是(即唯一的链接位于存储库本身) - 返回找到的对象

我需要这种行为的原因如下:创作Foo是一个费力的过程。此外,Foo它不是线程安全的,因此每个用户Foo必须有自己唯一的实例,这些实例可能之前已经创建和使用过。

似乎在任何地方都没有对对象的引用的“计数器”,因为垃圾收集器并不关心对特定对象的引用已经在那里声明了多少次。重要的是是否有这样的链接

所以我什至不知道是否有可能实现这样的东西C#......


升级版:

我无法选择要打勾的答案,因为iluxa1810和默认语言环境的答案对于一般情况更正确。但是,在我非常具体的任务框架内,约翰的回答中描述的方法......

c#
  • 3 3 个回答
  • 10 Views

3 个回答

  • Voted
  1. iluxa1810
    2020-11-06T17:08:28Z2020-11-06T17:08:28Z

    这是一个类似的问题,并以使用的形式提供了一个解决方案WeakReference- 这是对不会阻止垃圾收集的对象的引用。它有一个属性IsAlive,告诉对象是活着的还是已经被垃圾回收了。我看到你可以在你的任务中使用它。在您的情况下,只要有人从代码中引用它,该对象就会活着。

    无法检查代码中是否存在对对象的引用。.NET我们不再计算对对象的引用数,而是转而构建对象可用性图。

    作为替代方案,这是在您的存储中输入一个计数,强制用户调用 special。方法。但是这里一切都建立在信任之上...例如,他们突然忘记调用某事 => 该对象将为某人注册。

    • 2
  2. default locale
    2020-11-06T17:29:51Z2020-11-06T17:29:51Z

    也许网络中有一种机制可以以您编写的形式解决问题。但是,老实说,这个解决方案对我来说似乎并不明显且脆弱:

    • 不明显,因为 一段代码的结尾在阅读时很容易漏掉,因此很少用于关键操作:

      } //за одним символом здесь кроется важная операция по освобождению объекта.
      

      作为开发人员,我希望代码块可以自由重构。在这种情况下,有必要仔细监控所有变量的生命周期。Foo如果对其他类的引用存储在其他类的字段中(其对象本身将存储在集合中并在 lambdas 中捕获),情况将变得更加复杂。

    • 脆弱,因为 在这种情况下,逻辑将严重依赖于代码构建器的工作,这很难控制。

    替代方案:对象池

    为了解决您的问题(希望重用的昂贵对象),“对象池”设计模式是合适的。池广泛用于类似的任务(例如,存储数据库连接)。

    在最简单的版本中,您只需要:

    • 实现Foo接口IDisposable。
    • 跟踪创建和释放对象的storage方法调用。DisposeFoo
    • 在调用代码中,始终将派生storage对象包装在一个块中using
    • 为了实验的纯度,您可以将方法/属性移动Foo到单独的接口,该接口可以从storage. Foo让实现仅可用于,storage以便没有人可以覆盖这些方法。

    该解决方案很无聊,并且涉及更改调用代码。但是在这个版本中,释放对象的过程变得很明显。

    好吧,代码看起来像这样:

     using(var tmp0 = fooPool.GetFoo("tmp")) 
     {
          //работаем с одним объектом
     }
    
     using(var tmp1 = fooPool.GetFoo("tmp")) 
     {
          using(var tmp2 = fooPool.GetFoo("tmp")) 
          {
               //работаем с двумя объектами
          }
     }
    
    • 1
  3. Best Answer
    John
    2020-11-06T19:57:10Z2020-11-06T19:57:10Z

    您可以通过创建一个包装类来解决。

    我现在尽量简短,因为来自电话。

    底线是我们使用包装终结器来捕获包装的移除事件并让我们的实例重新启动并运行。

    让我们创建我们的包装类:

    class FooWrapper
    {
       public event Action<int> Final;
    
       private Foo foo;
       private int id;
    
       public FooWrapper(Foo foo, int id)
       {
          this.foo = foo;
          this.id = id;
       }
    
       // Вот и наш финализатор
       ~FooWrapper()
       {
           if (Final != null)
               Final(id)
       }
    }
    

    在程序本身中,我们创建了两个字典。一个用于未使用的 foo 实例,另一个用于已使用的实例。

    我认为逻辑或多或少是清楚的?

    1) 应用户的请求,在字典中搜索 Foo 的一个实例。在这种情况下,按 ID。

    2) 如果不是,则创建 Foo 并将其放置在第二个字典中。我们将它传递给 FooWrapper 并确保订阅 Final 事件。生成的 FooWrapper 已经提供给用户。

    3) 在用户丢失对 FooWrapper 的所有引用后,finalizer 触发,其中 Final 事件触发,传递我们的 Foo 的 ID。

    4)在主程序中,我们得到这个Id并将它从第二个字典转移到第一个。

    主要缺点是在这种情况下,我们完全排除了直接与 foo 交互的可能性,只能通过您在 fooWrapper 中编写的方法。

    • 1

相关问题

Sidebar

Stats

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

    是否可以在 C++ 中继承类 <---> 结构?

    • 2 个回答
  • Marko Smith

    这种神经网络架构适合文本分类吗?

    • 1 个回答
  • Marko Smith

    为什么分配的工作方式不同?

    • 3 个回答
  • Marko Smith

    控制台中的光标坐标

    • 1 个回答
  • Marko Smith

    如何在 C++ 中删除类的实例?

    • 4 个回答
  • Marko Smith

    点是否属于线段的问题

    • 2 个回答
  • Marko Smith

    json结构错误

    • 1 个回答
  • Marko Smith

    ServiceWorker 中的“获取”事件

    • 1 个回答
  • Marko Smith

    c ++控制台应用程序exe文件[重复]

    • 1 个回答
  • Marko Smith

    按多列从sql表中选择

    • 1 个回答
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Suvitruf - Andrei Apanasik 什么是空? 2020-08-21 01:48:09 +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