是否可以在不首先将字符转换为自己的字符串的情况下将字符插入字符串?

时间:2015-08-12 17:44:18

标签: c# string performance

我们正在开发一个对性能敏感的文本序列化类,我们希望尽可能避免将值类型转换为引用类型。

String.Insert方法似乎要求您提供字符串参数,并且没有允许将单个字符作为值类型传入的重载。

我们经常遇到这种情况,所以我想确保没有其他方法可以实现这一点,而无需将字符转换为自己的字符串,然后将其传递给String.Insert

我们已经考虑将父字符串视为基本数组,并从该角度插入单个字符 - 但这似乎也不起作用(除非我们做错了)。
这种方法的主要问题是它似乎要求我们使用String.AsCharArray方法,该方法将字符串的副本作为单独的引用对象生成 - 这是我们首先要避免的

3 个答案:

答案 0 :(得分:4)

  

生成字符串的副本作为单独的引用对象 - 这是我们首先要避免的。

没有创建新的字符串就无法修改字符串,除非我没有弄错,否则替换。您正在尝试使用已分配的内存调整字符串的大小。这就是为什么所有字符串方法都返回一个字符串而不修改原始字符串的原因。

答案 1 :(得分:1)

它可能没有比这简单得多:

public static string InsertChar( this string s , char c , int i )
{

  // create a buffer of the desired length
  int len = s.Length + 1 ;
  StringBuilder sb = new StringBuilder( len ) ;
  sb.Length = len ;

  int j = 0 ; // pointer to sb
  int k = 0 ; // pointer to s

  // copy the prefix to the buffer
  while ( k < i )
  {
    sb[j++] = s[k++] ;
  }

  // copy the desired char to the buffer
  sb[j++] = c ;

  // copy the suffix to the buffer
  while ( k < s.Length )
  {
    sb[j++] = s[k++] ;
  }

  // stringify it
  return sb.ToString();
}

或者这个

public static string InsertChar( this string s , char c , int i )
{
  StringBuilder sb = new StringBuilder( s.Length+1 ) ;
  return sb.Append( s , 0 , i ).Append( c ).Append( s , i , s.Length-i ) ;
}

你可以通过使用这样的不安全代码来加快速度(以避免范围检查的比较):

unsafe public static string InsertChar( this string s , char c , int i )
{
  if ( s == null ) throw new ArgumentNullException("s");
  if ( i < 0 || i > s.Length ) throw new ArgumentOutOfRangeException("i");

  char[] buf = new char[s.Length+1];

  fixed ( char *src = s )
  fixed ( char *tgt = buf )
  {
    int j = 0 ; // offset in source
    int k = 0 ; // offset in target

    while ( j < i )
    {
      tgt[k++] = src[j++];
    }

    tgt[k++] = c ;

    while ( j < s.Length )
    {
      tgt[k++] = src[j++] ;
    }

  }

  return new string( buf ) ;
}

如果你知道字符串相对较短,你可以使用stackalloc在堆栈而不是堆上分配工作缓冲区来加快速度。

答案 2 :(得分:0)

StringBuilder似乎是标准解决方案 它提供了一个更基本的字符串对象,作为标准字符数组,您可以反复操作而无需反复分配内存 然后,当您完成对StringBuilder对象的操作时,可以将其转换为标准字符串对象,仅为该字符串分配一次内存。

这仍然为字符串分配两次内存:一次用于StringBuilder,另一次用于最终的字符串对象。
但这是平台限制所能做到的最好的事情。

至少内存分配不再取决于您在序列化过程中经历的迭代次数 这是主要的优先事项,StringBuilder很好地解决了这个问题。

<强> <rant>
从性能和功能的角度来看,通过引用(或者通过const-reference)传递字符串是唯一在C ++中有意义的方法。
因此,.NET将字符串转换为按值传递的不可变引用类型这一事实对我来说似乎是一个C ++开发人员。 他们已经是参考类型了,对吧? 为什么我们不能像任何其他对象一样传递引用? 天啊! :)

我对微软的建议:
如果你的字符串对象不支持基本的字符串操作,那么你必须构建一个“hack”对象StringBuilder,封装一个像真正的字符串对象一样工作的标准字符串数组,以提供额外的功能,这是一个非常清楚的迹象,表明你的托管字符串对象很糟糕,需要自己纠正 的 </rant>