简体   繁体   English

使用弱引用和GC进行渲染

[英]Rendering using Weak References, and the GC

The Problem 问题

I've recently started learning C# . 我最近开始学习C# I am doing this through making a game (as I am quite familiar with this in C++ ). 我是通过制作游戏来做到这一点的(因为我在C++对此非常熟悉)。

Objects that are to be drawn to the back buffer are 'registered' upon construction via an event being sent with a weak reference to the object. 要绘制到后台缓冲区的对象在构造时通过使用对象的弱引用发送的事件进行“注册”。 With C++ smart pointers being reference counted and (as a result) objects being destroyed as soon as the last reference goes out of scope, this works well for my purposes. 由于C++智能指针被引用计数,并且(因此)一旦最后一个引用超出范围就被销毁,这对我的目的很有效。 However, in C# , this is certainly not the case. 但是,在C# ,情况肯定不是这样。

Consider the following code snippet: 请考虑以下代码段:

foreach(WeakReference<DrawableObject> drawable in drawables.ToList())
{
    DrawableObject target;
    drawable.TryGetTarget(out target);
    if(target != null)
    {
        spriteBatch.Draw(target.Texture, target.Position, target.Rectangle, target.Colour);
    }
    else
    {
        drawables.Remove(drawable);
    }
}

The main problem (as I'm sure there are most likely others I am yet to understand) with this in C# , is that target is not guaranteed to be null after the last strong reference to the object goes out of scope. C# ,主要问题(因为我确定很可能是其他我尚未理解的)是,在对象的最后一次强引用超出范围之后, target不能保证为null


Potential Solutions 潜在解决方案

Forcing garbage collection 强制垃圾收集

I could force invocation of the GC at an appropriate time beforehand, but after some research into the idea I have discovered that this is most likely a bad idea and/or a sign of other issues in the code. 我可以事先在适当的时间强制调用GC,但在对该想法进行一些研究后,我发现这很可能是一个坏主意和/或代码中其他问题的迹象。

Using a Hide() method 使用Hide()方法

Whilst such a method is essential to any game, having to explicitly call Hide() every single drawable object is tedious and gives more room for error. 虽然这种方法对于任何游戏都是必不可少的,但是必须明确地调用Hide()每个可绘制对象都是乏味的并且给出了更多的错误空间。 Furthermore, when should all these Hide() 's be called? 此外, 什么时候应该调用所有这些Hide() I cannot rely on the parent object being collected in time either, so placing them in a Finalizer would not solve the problem. 我也不能依赖于及时收集的父对象,因此将它们放在Finalizer并不能解决问题。

Implementing IDisposable 实现IDisposable

Similar to the above, with the alternative of a using statement (which I would not know where to place, since the objects need to be alive until their parents go out of scope). 与上面类似, using语句的替代using (我不知道在哪里放置,因为对象需要存活直到父母超出范围)。


The Question(s) 问题

None of the solutions I could think of seem to appropriately tackle the problem. 我能想到的解决方案似乎都没有适当地解决这个问题。 I of course missed out the idea of not using weak references in this way whatsoever, but will have to consider doing so if no proper solution is found that can work with the game in its current state. 我当然错过了不以这种方式使用弱引用的想法,但是如果找不到可以在当前状态下使用游戏的适当解决方案,则必须考虑这样做。 All this boils down to a few related questions: 所有这些归结为一些相关的问题:

  • Is this an appropriate use case for forcing garbage collection ? 这是强制垃圾收集的合适用例吗?

  • If not, how can I determine whether an object is due to be collected when the next run of the GC occurs? 如果没有,我怎么能确定一个对象是否是由于当GC下次运行时要收集的?

  • Is there a way to call a method of an object as soon as the last reference to it goes out of scope (either implicitly or explicitly), rather than waiting for the GC to come along? 有没有办法在对象的最后一次引用超出范围(隐式或显式)时调用对象的方法,而不是等待GC出现?

I am, of course, grateful for any suggestions that would avoid this problem altogether too. 当然,我对任何可以完全避免这个问题的建议表示感谢。

I'm going to attempt an answer at this.. 我打算在这个尝试答案..

What you're looking for is "I am dead according to the rules of the game" logic. 你要找的是“我按照游戏规则死了”的逻辑。 Not "That object is dead because the Garbage Collector says so" logic. 不是“那个对象已经死了,因为垃圾收集器说的那么”逻辑。

To that effect.. you need to flip your thinking to something like this: 为此...你需要将你的想法转变为这样的事情:

class DrawableObject {
    public bool IsDead { get; set; }
    public int Health { get; set; }

    // ...
    public void GetShot(int amount) {
        Health -= amount;

        if (Health <= 0)
            IsDead = true;
    }
}

..then: ..然后:

foreach (var drawable in drawables) {

    DrawableObject target;
    drawable.TryGetTarget(out target);

    if(target == null || target.IsDead) {
        drawables.Remove(drawable);
    }
    else {
        spriteBatch.Draw(target.Texture, target.Position, target.Rectangle, target.Colour);
    }
}

If its null based on your TryGetXXX logic.. or it has been marked as dead by your game logic.. remove it from the list of drawable objects. 如果它的null基于你的TryGetXXX逻辑..或者它被你的游戏逻辑标记为死了..将它从可绘制对象列表中删除。 Let the garbage collector clean it up whenever it wants after that. 让垃圾收集器随时清理它。

TLDR: Mark your objects as dead and base your logic around your own "I am dead" flag. TLDR:将你的对象标记为死亡,并将你的逻辑基于你自己的“我死了”的旗帜。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM