简体   繁体   English

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

[英]Mutable strings in WP7 C#/XNA?

I'm working on a Windows Phone 7 XNA game. 我正在开发一款Windows Phone 7 XNA游戏。 It's a port of a game written in C++, and as such, I'm trying to do as little rewriting of gameplay code as possible. 它是用C ++编写的游戏的端口,因此,我试图尽可能少地重写游戏代码。

Garbage is a huge problem on WP7, because the collector is nongenerational and slow, so a collection (which is triggered every 1MB) takes about 10ms per MB of allocations. 垃圾是WP7上的一个问题,因为收集器是非代数且速度慢的,因此一个集合(每1MB触发一次)每MB分配大约需要10ms。 I fully intend to be using the maximum 90MB available, so we're looking at a ~900ms stall every MB of allocation. 我完全打算使用最大可用的90MB,因此我们正在考虑每MB分配约900ms的停顿时间。

I've been able to rework things so that we don't have garbage generated per-frame, except for a few cases of strings. 我已经能够重做工作,因此我们没有每帧生成垃圾,除了一些字符串的情况。

It seems that StringBuilder.ToString() generates garbage, and the method described here doesn't work on WP7. 似乎StringBuilder.ToString()生成垃圾, 这里描述的方法不适用于WP7。

The two things I need to do are: 我需要做的两件事是:

  • Format minutes/seconds/hundreths as mm:ss.hh for display to the screen. 格式化分钟/秒/ hundreths为mm:ss.hh以显示在屏幕上。 Apparently I can do that with a StringBuilder (using extension methods that don't create garbage from boxing the ints) and display the StringBuilder directly with SpriteBatch. 显然,我可以使用StringBuilder(使用不会因装箱而创建垃圾的扩展方法)并使用SpriteBatch直接显示StringBuilder。
  • Split a string of the form "foo.bar.baz.qux" into an array on '.', ie {"foo", "bar", "baz", "qux"} and copy one element at a time into an array of strings. 将“foo.bar.baz.qux”形式的字符串拆分为“。”上的数组,即{“foo”,“bar”,“baz”,“qux”},并将一个元素一次复制到字符串数组。 This is for setting the hierarchical state of game actors. 这用于设置游戏角色的层次状态。 It's also pretty much directly ported from the original game, and quite a bit depends on it working this way. 它也几乎直接从原始游戏移植过来,而且很大程度上依赖于它以这种方式工作。 I'd really like to avoid rewriting it. 我真的想避免重写它。

Short of converting a lot of code to use char[] instead of string, is there any way to have truly garbage-free mutable strings in C#? 如果没有将大量代码转换为使用char []而不是字符串,那么有没有办法在C#中拥有真正无垃圾的可变字符串?

Why do you need to convert a StringBuilder to a String in the first place? 为什么首先需要将StringBuilder转换为String As you've noted, you can pass StringBuilder directly into XNA's drawing methods. 如您所知,您可以将StringBuilder直接传递给XNA的绘图方法。 You can also use StringBuilder to retrieve substrings: 您还可以使用StringBuilder来检索子字符串:

substringBuilder.Length = 0;
for (int i = start; i <= end; i++)
    substringBuilder.Append(originalBuilder[i]);

The only things you want to avoid with StringBuilder are ToString() and AppendFormat() . StringBuilder唯一要避免的是 ToString()AppendFormat() None of the other formatting methods (to my knowledge) generate garbage. 没有其他格式化方法(据我所知)生成垃圾。 Update: I was wrong. 更新:我错了。 Write your own extension methods. 编写自己的扩展方法。 A rundown is available here: Avoiding garbage when working with StringBuilder 这里提供了一个简介: 使用StringBuilder时避免垃圾

It's relatively easy to write a method that will split a string into substrings based on a given delimiter. 编写一个方法会相对容易,该方法会根据给定的分隔符将字符串拆分为子字符串。 Just remember that String.Split() is evil for two reasons -- first, because it allocates new strings; 请记住String.Split()是邪恶的,原因有 :首先,因为它分配了新的字符串; and second, because it allocates a new array . 第二,因为它分配了一个新数组 The immutability of strings is only half of your problem. 字符串的不变性只是你问题的一半。

Consider the following extension methods for StringBuilder . 请考虑StringBuilder的以下扩展方法。 I haven't thoroughly tested this code, but it should give you a general idea. 我还没有彻底测试过这段代码,但它应该给你一个大致的想法。 Note that it expects you to pass in a pre-allocated array, which it will then populate. 请注意,它希望您传入一个预先分配的数组,然后填充该数组。

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;
}

I'm trying to do as little rewriting of gameplay code as possible. 我试图尽可能少地重写游戏代码。

Careful with that it may take longer that rewrite. 小心,重写可能需要更长时间。 Cole Campbell is right, there is no point in calling ToString() while you want to draw it. Cole Campbell是对的,在你想要绘制ToString()时没有任何意义。 XNA has draw method that takes StringBuldier and it works well in my game. XNA有draw方法,它采用StringBuldier,在我的游戏中运行良好。

Another solution to your problem: make a global HashSet of your strings and set up it when you load the game or level to avoid collection pressure. 问题的另一个解决方案:创建字符串的全局HashSet并在加载游戏或级别时设置它以避免收集压力。

Try to do as much as possible when you load the game. 加载游戏时尽可能多地尝试。

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

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