WP7 C#/XNA中的可变字符串?

10

我正在开发一个Windows Phone 7 XNA游戏。这是一个由C++编写的游戏的移植版本,因此,我尽量少地重写了游戏代码。

垃圾问题在WP7上是一个巨大的问题,因为收集器是非分代和慢速的,因此每1MB的内存分配会触发一次垃圾回收。每MB的内存分配需要约10ms的时间。我希望使用最大的90MB可用内存,所以我们需要面对大约每MB内存分配都要停顿约900ms的情况。

我已经重新设计了一些东西,使得我们不会在每帧中生成垃圾,除了一些字符串的情况。

似乎StringBuilder.ToString()会生成垃圾,并且这里描述的方法在WP7上并不适用。

我需要做的两件事是:

  • 将分钟/秒/百分之一秒格式化为mm:ss.hh以显示在屏幕上。显然,我可以使用StringBuilder(使用不会创建垃圾的扩展方法来转换int),并直接使用SpriteBatch显示StringBuilder。
  • 将形式为"foo.bar.baz.qux"的字符串拆分成以'.'为分隔符的数组,即{"foo","bar","baz","qux"},并将一个元素一个接一个地复制到字符串数组中。这是为了设置游戏角色的层级状态。它也基本上直接从原始游戏移植过来,很多东西都依赖于它按这种方式工作。我真的希望避免重写它。

除了将大量代码转换为char[]而不是字符串之外,有没有办法在C#中拥有真正无垃圾的可变字符串?


2
只是一点小提示,Mango拥有分代GC功能。http://blogs.msdn.com/b/abhinaba/archive/2011/04/13/generational-gc-in-windows-phone-mango.aspx - Terrance
这也是将某些东西“接近”可变字符串的一种方法:http://stackoverflow.com/questions/6913435/how-to-get-a-list-of-mutable-strings - Terrance
2个回答

10
为什么你需要首先将一个 StringBuilder 转换为 String 呢?正如你所提到的,你可以直接传递 StringBuilder 到 XNA 的绘图方法中。你也可以使用 StringBuilder 来检索子字符串:
substringBuilder.Length = 0;
for (int i = start; i <= end; i++)
    substringBuilder.Append(originalBuilder[i]);

只有 ToString()AppendFormat() 是需要避免使用 StringBuilder 的。据我所知,其他格式化方法(如果有的话)都不会产生垃圾。 更新:我错了。自己编写扩展方法。你可以在这里找到一个概述:当使用 StringBuilder 时避免垃圾

编写一个将字符串基于给定分隔符拆分为子字符串的方法相对容易。但请记住,String.Split()两个问题——首先它会分配新的字符串;其次,它会分配新的数组。字符串的不可变性只是问题的一半。

考虑下面的 StringBuilder 扩展方法。我没有仔细测试过这段代码,但它应该可以给你一个大致的想法。请注意,它期望你传入一个预先分配的数组,然后填充它。

public static void Substring(this StringBuilder source, Int32 start, Int32 count, StringBuilder output)
{
    output.Length = 0;
    for (int i = start; i < start + count; i++)
    {
        output.Append(source[i]);
    }
}

public static int Split(this StringBuilder source, Char delimiter, StringBuilder[] output)
{
    var substringCount = 0;
    var substringStart = 0;
    for (int i = 0; i < source.Length; i++)
    {
        if (source[i] == delimiter)
        {
            source.Substring(substringStart, i - substringStart, output[substringCount]);
            substringCount++;
            substringStart = i + 1;
        }
    }
    if (substringStart < source.Length - 1)
    {
        source.Substring(substringStart, source.Length - substringStart, output[substringCount]);
        substringCount++;
    }
    return substringCount;
}

1
格式化非字符串对象(例如数字)将生成垃圾(它调用它们的“ToString”方法)。但编写一个无垃圾的扩展方法以追加整数(或大多数其他类型)相当简单。你可以很容易地在网上找到示例。 - Andrew Russell
快速浏览反编译源代码确认了你的正确性。我不确定为什么我之前认为不是这样。但是,是的,你可以绕过这个问题。 - Cole Campbell

0
我尽可能地想少重写游戏代码。
小心,这可能比重写更耗时间。Cole Campbell 是对的,如果你想要绘制它,调用 ToString() 没有意义。XNA 有 Draw 方法可以使用 StringBuilder,在我的游戏中效果很好。
解决您的问题的另一个方法是:创建一个全局 HashSet 来存储您的字符串,并在加载游戏或关卡时设置它,以避免收集压力。
尽量在加载游戏时完成尽可能多的工作。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接