0

最近我开始使用游戏状态管理(详情:create.msdn.com/en-US/education/catalog/sample/game_state_management),这是用 XNA 制作的简单游戏的绝佳模板。

几天来我一直在分析它的实现,我对这种方法的LoadingScreen.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

我在上面的代码中提出了问题和疑问(见评论)。

4

3 回答 3

1

这完全取决于编写库的人的口味,但这可能是因为该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 是正在加载的屏幕。

在这个用例中,调用代码实际上并没有对数组的引用,因此对其进行克隆将毫无意义地浪费时间和内存。它的内容从你下面被改变的可能性很小。

于 2012-05-04T09:42:58.597 回答
0

对screensToLoad 进行浅拷贝是有意义的(仅复制数组),但正如在this other answer中指出的那样,不这样做通常不会造成麻烦。

制作深拷贝是错误的。屏幕是有状态的对象,如果您复制它们,用户将无法获得他们通常期望的效果。例如,在调用后注册到原始屏幕的所有处理程序Load都将注册到“死”屏幕,因为它们ScreenManager将持有副本,而不是原始屏幕。

于 2012-05-04T19:27:19.957 回答
0

创建一个屏幕对象以由屏幕管理器管理...

克隆没有意义,因为原始屏幕将变得无用...

屏幕管理器最好是一个工厂,按类型创建屏幕并返回一个 id 或要使用自定义参数初始化的屏幕......

但我认为这段代码来自一个示例来学习,不应该期望有一个复杂的代码......

于 2012-05-04T19:41:54.600 回答