C#中如何正确的使用字符串String

前言

C#中提供了比较全面的字符串处理方法,很多函数都进行了封装为我们的编程工作提供了很大的便利。System.String是最常用的字符串操作类,可以帮助开发者完成绝大部分的字符串操作功能,使用方便。

字符串作为所有编程语言中使用最频繁的一种基础数据类型。如果使用不慎,将会造成不必要的内存开销,为此而付出代价。

而要优化此类型,从以下两点入手:

1、尽量少的装箱

2、避免分配额外的内存空间

先从第一点装箱的操作说起,查看如下代码:

 //发生装箱的代码
 String boxOperate = "test" + 4.5f;

其中间语言IL代码为如下:

 IL_0000: nop
 IL_0001: ldstr "test"
 IL_0006: ldc.r4 4.5
 IL_000b: box [mscorlib]System.Single
 IL_0010: call string [mscorlib]System.String::Concat(object, object)
 IL_0015: stloc.0
 IL_0016: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
 IL_001b: pop
 IL_001c: ret

不难看出,上述代码发生了装箱的操作(IL代码中的box).装箱之所以会发生性能损耗,因为它要完成如下三个步骤:

1、首先,会为值类型在托管堆中分配内存。除了值类型本身所分配的内存外,内存总量还要加上类型对象指针和同步块索引所占用的内存,

2、将值类型的值复制到新分配的堆内存中。

3、返回已经成为引用类型的对象的地址。

在来看以下代码:

//没有发生装箱的代码
 String boxOperate = "test" + 4.ToString();

其中间IL代码如下:

 IL_0000: nop
 IL_0001: ldstr "test"
 IL_0006: ldc.r4 4
 IL_000b: stloc.1
 IL_000c: ldloca.s 1
 IL_000e: call instance string [mscorlib]System.Single::ToString()
 IL_0013: call string [mscorlib]System.String::Concat(string, string)
 IL_0018: stloc.0
 IL_0019: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()
 IL_001e: pop
 IL_001f: ret

如上,并没有发生任何装箱操作,但是达到的结果却是我们想要的。原因是 4.ToString() 这行代码并没有发生装箱行为,是实际调用的是整数型的ToString()方法,其原型如下:

 public override string ToString(){
  return Number.FormatInt32(m_value, null, NumberFormat.CurrentInfo);
 }

可能有人会问,是不是原型中的 Number.Format_XXX方法会发生装箱行为呢?实际上,Number.Format_XXX方法是一个非托管的方法,其原型如下:

[MethodImpl(MethodImplOptions.InternalCall), SecurityCritical]
public statuc extern string FormatInt32(int value, string format,NumberFormatInfo info);

它是通过直接操作内存来完成 Int32 到 String 的转换,效率要比装箱高得多。所以,在使用其他值引用类型到字符串得转换比完成拼接时,应当避免使用操作符 “+” 来我完成,而应该使用值引用类型提供得ToString方法。

也许有人会问:即使FCL提供得方法没有发生装箱行为,但在其他情况下,FCL方法内部会不会含有装箱的行为?也许会存在,所以,本人推荐:编写代码中,应当尽量避免发生不必要的装箱代码。

第二个方面:避免分配额外的空间。对于CLR来说,String对象(字符串对象)是个很特殊的对象,它一旦被赋值就不可改变(在内存中)。在运行时调用System.String类中的任何方法或进行任何运算('=‘赋值,'+‘拼接等),都会在内存中创建一个新的字符串对象,这也意味着要为该新对象分配新的内存空间。如以下代码会带来额外开销。

private static void Test(){
   String str1 = "aa";
  str1 = str1 + "123" + "345";
   //以上代码创建了3个String对象,并执行了一次String.Contact方法。
}

而在以下代码中,字符串不会在运行时拼接字符串,而是会在编译时直接生成一个字符串。

private static void Test()
{
String str= "aa" + "123" + "345";//等效 String str= "aa123345";
}

private static void Test2()
{
const String str = "aa";
String newStr = "123" + str;
//因为str是一个常量,所以该代码等效于 String newStr = "123" + “aa”;
//最终等效于 String newStr = "123aa”;
}

由于使用System.String类会在某些场合带来明显的性能损耗,所以微软另外提供了一个类型StringBuilder来弥补String的不足。

StringBuilder并不会重新创建一个String对象,它的效率源于预先以非托管的方式分配内存。如果StringBuilder没有先定义长度,则默认分配的长度为16。当StringBuilder的长度大于16小于32时,StringBuild又会重新分配内存,使之成为16的倍数。StringBuilder重新分配内存时按照上次的容量加倍进行分配的。注意:StringBuilder指定的长度要合适,太小了,需要频繁分配内存;太大了,浪费内存空间。

以下是例子举例:

private static String Test3()
  {
   String a = "t";
   a += "e";
   a += "s";
   a += "t";
   return a;
  }
  private static String Test4()
  {
   String a = "t";
   String b = "e";
   String c = "s";
   String d = "t";
   return a + b + c + d;
  }
  //以上两种效率都不高效。不要以为前者比后者创建的字符串对象更少,事实上,两者创建的字符串对象相等
  //且前者进行了3次的String.Contact方法调用,比后者还多了两次。

要完成上图的运行时的字符串拼接(注意:是运行时),更佳的做法是使用StringBuilder类型,代码如下:

private static String Test5()
  {
   String a = "t";
   String b = "e";
   String c = "s";
   String d = "t";
   StringBuilder sb = new StringBuilder(a);
   sb.Append(b);
   sb.Append(c);
   sb.Append(d);
   return sb.ToString();
   //因为说的是运行时,所以没必要使用以下代码
   //StringBuilder sb = new StringBuilder("t");
   //sb.Append("e");
   //sb.Append("s");
   //sb.Append("t");
   //return sb.ToString();
  }

微软还提供了另外一个来简化这种操作,即使用String.Format 方法。String.Format方法在内部使用StringBuilder 进行字符串格式化,如下图代码:

private static String Test6()
{
  //为演示,定义4个变量
  String a = "t";
  String b = "e";
  String c = "s";
  String d = "t";
  return String.Format("{0}{1}{2}{3}", a, b, c, d);
}

总结:

在使用String字符串时,应该尽量避免装箱操作和“+”连接操作。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • C# 6.0 内插字符串(Interpolated Strings )的使用方法

    看Interpolated Strings之前,让我们先看EF Core 2.0 的一个新的特性:String interpolation in FromSql and ExecuteSqlCommand. var city = "London"; using (var context = CreateContext()) { context.Customers .FromSql($@" SELECT * FROM Customers WHERE City = {city}&

  • C# string格式的日期时间字符串转为DateTime类型的方法

    方法一:Convert.ToDateTime(string) string格式有要求,必须是yyyy-MM-dd hh:mm:ss 方法二:Convert.ToDateTime(string, IFormatProvider) DateTime dt; DateTimeFormatInfo dtFormat = new System.GlobalizationDateTimeFormatInfo(); dtFormat.ShortDatePattern = "yyyy/MM/dd";

  • C#、.Net中把字符串(String)格式转换为DateTime类型的三种方法

    方式一:Convert.ToDateTime(string) 复制代码 代码如下: Convert.ToDateTime(string) 注意:string格式有要求,必须是yyyy-MM-dd hh:mm:ss 方式二:Convert.ToDateTime(string, IFormatProvider) 复制代码 代码如下: DateTimeFormatInfo dtFormat = new System.GlobalizationDateTimeFormatInfo(); dtFormat

  • C#中字符串优化String.Intern、IsInterned详解

    前言 string是一种很特殊的数据类型,它既是基元类型又是引用类型,在编译以及运行时,.Net都对它做了一些优化工作,正式这些优化工作有时会迷惑编程人员,使string看起来难以琢磨.本文将给大家详细介绍关于C#字符串优化String.Intern.IsInterned的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 首先看一段程序: using System; class Program { static void Main(string[] args) { st

  • C#之CLR内存字符串常量池(string)

    C#中的string是比特殊的类,说引用类型,但不存在堆里面,而且String str=new String("HelloWorld")这样的重装也说没有的. 我们先来看一个方法: class Program { static void Main(string[] args) { String s = "HelloWorld"; Console.WriteLine(s); } } 然后我们用ildasm.exe工具把它生成IL语言来看一看它里面是怎么玩的: .met

  • C#中把字符串String转换为整型Int的小例子

    本文介绍如何在使用C#开发程序时,将一个字符串String变量的值转换为一个整型Int变量. 比如,我们在C#中定义一个字符串变量,用它来获取一个xml中的值.小编这里并不是故意要用一个字符串去获取xml节点的值,而是使用InnerText的方式获取的值必须是字符串String类型的. 复制代码 代码如下: string tmpValue = ""; tmpValue = xml.DocumentElement["expirydays"].InnerText.Tri

  • C#中如何正确的使用字符串String

    前言 C#中提供了比较全面的字符串处理方法,很多函数都进行了封装为我们的编程工作提供了很大的便利.System.String是最常用的字符串操作类,可以帮助开发者完成绝大部分的字符串操作功能,使用方便. 字符串作为所有编程语言中使用最频繁的一种基础数据类型.如果使用不慎,将会造成不必要的内存开销,为此而付出代价. 而要优化此类型,从以下两点入手: 1.尽量少的装箱 2.避免分配额外的内存空间 先从第一点装箱的操作说起,查看如下代码: //发生装箱的代码 String boxOperate = "

  • Java中char数组(字符数组)与字符串String类型的转换方法

    本文实例讲述了Java中char数组(字符数组)与字符串String类型的转换方法.分享给大家供大家参考,具体如下: 在Java语言编程时,使用"口令字段"jPasswordField组件时,如果要获得密码值,就需要使用该组件的getPassword()方法.jPasswordField的getPassword()方法返回一个char类型的数组,我们经常需要将这个数组转换为String类型,以便进行诸如口令匹配或口令赋值等操作.这时,就需要将char类型的数组进行转换.当然也经常会遇到

  • 浅谈javascript中字符串String与数组Array

    简单点就是string是字符(串)... 而array是数组...可以放数字啊,字符啊等一系列东东!!! 上个示例: 复制代码 代码如下: var str = "liuzhanqi"; document.write(str["length"]);//等价str.l ength  var str = string.fromcharcode(72, 101, 108, 108, 111, 33); document.write(str); //各整数作为unicode编

  • ES6中字符串string常用的新增方法小结

    本文实例讲述了ES6中字符串string常用的新增方法.分享给大家供大家参考,具体如下: ES6为js新增了很多方法,包括遍历.查询.替换等等,可以很简洁的替换ES5中的类似方法,本文不考虑codePointAt等不常用方法. for-of: let str="wbiokr"; for(let s of str){ console.log(s) } //结果:w, b, i, o, k, r 由于es5并没有为js制定字符串相关遍历方法,for-of无疑会是接下来前端开发中的一个很重要

  • Python中字符串String的基本内置函数与过滤字符模块函数的基本用法

    首先我们要明白在python中当字符编码为:UTF-8时,中文在字符串中的占位为3个字节,其余字符为一个字节 下面就直接介绍几种python中字符串常用的几种字符串内置函数(本文中牵扯到了模块与一些之前章节没讲过的相关知识,坑我之后会填的) 字符串切片(截取字符串): #字符串切片 string[开始位置:结束位置:步长] name = "巩祎鹏"print(name[0:]) #从第一个字符截取到最后一个字符 print(name[0:2]) #从第一个字符截取到第二个字符 prin

  • C++ 读文件 将文件内容读入到字符串string中的方法

    如下所示: #include <string> #include <fstream> #include <sstream> #include <iostream> #include <stdlib.h> using namespace std; //从文件读入到string里 string readFileIntoString(char * filename) { ifstream ifile(filename); //将文件读入到ostring

  • Java中拼接字符串String的N种方法总结

    目录 1.前言 2.问题复现 3.使用+运算符 4.使用String.concat() 5.使用StringBuilder 6.使用StringJoiner类(Java8+) 7.使用Streams.filter(Java8+) 8.总结 1. 前言 Java 提供了拼接 String 字符串的多种方式,不过有时候如果我们不注意 null 字符串的话,可能会把 null 拼接到结果当中,很明显这不是我们想要的. 在这篇文章中,将介绍一些在拼接 String 时避免 null 值的几种方式. 2.

  • python中字符串String及其常见操作指南(方法、函数)

    目录 下标与切片 常见方法 查找 替换 分割 大小写格式化 对齐格式化 清除空白字符 检查 补充:16个常用函数 总结 注意:对字符串的所有操作,原始数据(即原字符串)不变 !(字符串是不可变类型) 对原字符串操作会返回一个操作后的数据, 可以使用变量去接受数据 ----------------------------------------------------------------------------------------------------------------------

  • Golang中字符串(string)与字节数组([]byte)一行代码互转实例

    目录 一.字符串与字节数组? 二.详细代码 1.简单的方式字节转字符串 2.简单的字符串转字节数组 3.字节转字符串 4.字符串转字节数组 5.完整运行测试 补充:一些结论如下 总结 一.字符串与字节数组? 字符串是 Go 语言中最常用的基础数据类型之一,本质上是只读的字符型数组,虽然字符串往往都被看做是一个整体,但是实际上字符串是一片连续的内存空间. Go 语言中另外一个类型字节(Byte).在ASCII中,一个英文字母占一个字节的空间,一个中文汉字占两个字节的空间.英文标点占一个字节,中文标

随机推荐