是否可以将一个字符插入字符串而不首先将该字符转换为它自己的字符串?

本文关键字:字符串 字符 自己的 它自己 转换 插入 是否 一个 | 更新日期: 2023-09-27 18:07:50

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

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

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

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

是否可以将一个字符插入字符串而不首先将该字符转换为它自己的字符串?

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

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

可能没有比这更简单的了:

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在堆栈上而不是在堆上分配工作缓冲区来加快速度。

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

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

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

<rant>
从性能和功能的角度来看,通过引用(或const-reference)传递字符串是c++中唯一有意义的方法。
因此,作为一个c++开发人员,我觉得。net将字符串变成不可变的引用类型并按值传递的事实是如此的落后。
它们已经是引用类型了,对吧?
为什么我们不能像传递其他对象一样传递引用呢?天啊!:)

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