XNA 游戏状态管理引用分配
本文关键字:引用 分配 管理 状态 游戏 XNA | 更新日期: 2023-09-27 18:33:10
最近我开始使用游戏状态管理(详细信息:create.msdn.com/en-US/education/catalog/sample/game_state_management),这是XNA制作的简单游戏的绝佳模板。
几天来我一直在分析它的实现,我对 LoadScreen .cs 在这种方法中表示怀疑:
/// <summary>
/// The constructor is private: loading screens should
/// be activated via the static Load method instead.
/// </summary>
private LoadingScreen(ScreenManager screenManager, bool loadingIsSlow,
GameScreen[] screensToLoad)
{
this.loadingIsSlow = loadingIsSlow;
this.screensToLoad = screensToLoad;
TransitionOnTime = TimeSpan.FromSeconds(0.5);
}
我不明白为什么会有引用分配:this.screensToLoad = screensToLoad;
.为什么不使用类似.Clone()
方法?
[编辑]
还行。。。我认为我的问题不是 XNA 或游戏状态管理,我准备了一段代码,解释了我的疑问。
代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace test
{
public class a
{
public int number = 3;
}
public class b
{
public a tmp;
public void f(a arg)
{
tmp = arg; // (*?*) isn't it dangerous assigning?
}
}
class Program
{
static void Main(string[] args)
{
b temp_b = new b();
{// (*!*) CODE BLOCK COMES HERE:
a temp_a = new a();
temp_b.f(temp_a);
temp_a.number = 4;
}
// TROUBLE EXPLANATION:
// We are outside of code block which I marked with (*!*)
// Now reference 'temp_a' is inaccessible.
// That's why line of code which I marked with (*?*) is dangerous.
// We saved 'temp_a' which is no longer accessible in 'temp_b' object.
//
// Now computer's memory pointed by reference, which is saved in 'temp_b.tmp' (and was saved in 'temp_a'),
// can be overriden by code which comes somewhere below this comment (or even in another thread or process).
//
// I think the same situation is in XNA GSM's piece of code.
// The only solution in my opinion is deep copy (AFAIK .Clone() can be implemented as shallow or deep copy).
Console.WriteLine(temp_b.tmp.number); // result is 4
// because we copied reference
// For me it's strange that this line was printed. As I mentioned above
// memory intended for 'temp_a' could be reused and overwritten.
}
}
}
为方便起见,此处相同的代码:ideone.com/is4S3。
我在上面的代码中提出了问题和疑问(请参阅注释)。
这种事情完全取决于编写库的人的口味,但这可能是因为 Load()
方法具有以下签名:
public static void Load(ScreenManager screenManager, bool loadingIsSlow,
PlayerIndex? controllingPlayer,
params GameScreen[] screensToLoad)
请注意,screensToLoad
是使用 params
关键字定义的。 这意味着你应该这样称呼它:
LoadingScreen.Load(manager, false, null, s1, s2, s3, s4, s5);
其中 s1 ...sN 是正在加载的屏幕。
在此用例中,调用代码实际上并没有对数组的引用,因此克隆它将毫无意义地浪费时间和内存。 它的内容从你下面被改变出来的机会相当小。
创建屏幕对象以由屏幕管理器管理...
克隆没有意义,因为原始屏幕将变得毫无用处......
也许屏幕管理器是一个工厂更好,按类型创建屏幕并返回一个 id 或要用自定义参数初始化的屏幕......
但我认为这段代码来自一个示例来学习,不应该期望有一个复杂的代码......
做一个 screensToLoad 的浅拷贝(只复制数组)是有意义的,但正如另一个答案所指出的,不这样做通常不会造成麻烦。
制作深拷贝是错误的。屏幕是有状态的对象,如果您复制它们,用户将无法获得他们通常期望的效果。例如,在调用Load
后注册到原始屏幕的所有处理程序都将注册到"死"屏幕,因为ScreenManager
将持有副本,而不是原始副本。