使用弱引用和GC进行渲染 [英] Rendering using Weak References, and the GC
问题描述
问题
我最近开始学习 C#
。我是通过制作游戏来做到这一点的(因为我很熟悉 C ++
)。
对象将被绘制到后台缓冲区,在施工时通过对该对象的弱引用发送的事件进行注册。使用 C ++
智能指针作为引用计数,并且(因此)只要最后一个引用超出范围就会销毁对象,这对我的目的非常有用。然而,在 C#
中,这当然不是这种情况。
考虑下面的代码片段:
foreach(WeakReference< DrawableObject> drawable in drawables.ToList())
不能保证是<$
{
DrawableObject目标;
drawable.TryGetTarget(out target);
if(target!= null)
{
spriteBatch.Draw(target.Texture,target.Position,target.Rectangle,target.Colour);
}
else
{
drawables.Remove(drawable);
$ / code $ / pre
主要问题(因为我确定有最有可能的其他人我还没有理解)在
C#
中,target
$ b
null 在对象的最后一个强引用超出了作用域之后。 h2>潜在解决方案
强制垃圾回收
我可以强制调用GC在适当的时间提前,但经过对这个想法的一些研究后,我发现这很可能是一个坏主意和/或代码中其他问题的迹象。
<使用Hide()方法
虽然这种方法对于任何游戏都很重要,但必须显式调用
Hide()
每一个可绘制的对象都很乏味,给出了更多的错误空间。此外,当应该调用所有这些Hide()
吗?我不能依靠收集的父对象,因此在Finalizer
中播放它们不会解决问题。
实现IDisposable
与上面类似,使用 >声明(我不知道在哪里放置,因为对象需要活着直到父母超出范围)。
$ b问题
我所能想到的解决方案似乎都不能解决问题。我当然错过了不以这种方式使用弱引用的想法,但是如果没有找到可以在当前状态下与游戏一起工作的适当解决方案,则必须考虑这样做。所有这些都归结为一些相关的问题:
strong>?
这是强制垃圾收集的合适用例
如果不是,我如何确定一个对象是否是到期当GC的下一次运行发生时?
有没有办法在最后一次调用对象的方法
的引用超出了范围(隐式或显式地),
而不是等待GC出现?
ul>
我当然非常感谢任何可以避免这个问题的建议。
解决方案我会在此尝试一个答案。
你在找什么是我死了根据规则的游戏的逻辑。不是那个对象已经死了,因为垃圾收集器这么说逻辑。
为了达到这个效果,你需要把你的想法转换成这样的东西:
class DrawableObject {
public bool IsDead {get;组; }
public int Health {get;组; }
// ...
public void GetShot(int amount){
Health - = amount;
if(Health <= 0)
IsDead = true;
..然后:
foreach(drawable中的var drawable){
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);
如果它的null code>基于你的
TryGetXXX
逻辑......或者它已经被你的游戏逻辑标记为死亡了......将它从可绘制对象列表中移除。让垃圾收集器随时清理它。
TLDR:将您的对象标记为已死,并将您的逻辑基于您自己的我已死标志。 / p>
The Problem
I've recently started learning
C#
. I am doing this through making a game (as I am quite familiar with this inC++
).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. However, inC#
, this is certainly not the case.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 thattarget
is not guaranteed to benull
after the last strong reference to the object goes out of scope.
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.
Using a Hide() method
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. Furthermore, when should all theseHide()
's be called? I cannot rely on the parent object being collected in time either, so playing them in aFinalizer
would not solve the problem.Implementing 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).
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?
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?
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 yourTryGetXXX
logic.. or it has been marked as dead by your game logic.. remove it from the list of drawable objects. 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.
这篇关于使用弱引用和GC进行渲染的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!