首页 未命名正文

linux编程_C#基础知识之字符串

云返利网 未命名 2020-05-26 09:07:09 14 0

 string作为我们在编程当中用的最多的数据类型,同时又由于它的特殊性,怎么强调它的重要性都不为过,明白string的一些类型和存储机制,有助于我们写出准确且高效的代码.

一.string类型

    1.string的类型

    string类型直接继续Object类型,Object类型是引用类型,因而string类型是引用类型无疑.

  我们借助VS的类视图可以看到这一点:

       

         这意味着:

      (a).string类型不会在线程的客栈中存储任何字符串,而是存储在堆上

      (b).未初始时,它被设置为null

      PS:在内部,string是用字符串char的聚集来维护的

      2.string声明的IL形貌

   IL,组织新实例的IL指令是newobj,是不是string也是这样?

 

   我们使用如下代码:

 1 class Program
 2 {
 3      static void Main(string[] args)
 4     {
 5          string str = "Hello World!";
 6          string str2 = "Hello" + " My" + " World!";
 7           Person person = new Person();
 8      }
 9  }
10 
11 class Person
12 {
13     string Name;
14 }

我们查看IL代码如下:

      可以看出

      (a).对比13,组织Person工具使用了newobj指令,然则在组织字符串的时刻,使用了专门的ldstr(load string)指令

      (b).更进一步,编译器将这些字面值字符串放到模块的元数据中,在运行时加载和引用它们

      (c).2,对于使用+相符将各literal毗邻起来的写法,编译器在编译的历程中会直接毗邻他们.

二.string的操作带来的疑问

    OK,通过第1部门,我们知道了,string是引用类型,它存储在堆中.

    我们知道对于引用类型,赋值操作=会通报的是引用,不是值,但组织差别的引用类型时通常它们的引用也差别.如下面这种:

 1 class Program
 2 {
 3      static void Main(string[] args)
 4      {
 5            //Person实害?例
 6            Person person1 = new Person("A");
 7            Person person2 = new Person("A");
 8            Console.WriteLine(object.ReferenceEquals(person1, person2));
 9 
10            //string
11            string str1 = "Hello World!";
12            string str2 = "Hello World!";
13            string str3 = "Hello " + "World!";
14            Console.WriteLine(object.ReferenceEquals(str1, str2));
15            Console.WriteLine(object.ReferenceEquals(str1, str3));
16 
17            Console.Read();
18        }
19 }
20 
21class Person
22 {
23    public Person(string strName)
24    {
25 
26    }
27

    我们先给出运行效果:

     我们知道object.ReferenceEquals是对照两个工具的引用是否一样,对于第1种Person的情形,我们可以明白,由于他们都是组织了差别的工具,引用的存储地址也是差别的.但对于第2,3,string就像成为了值类型一样,返回了True,那么问题来了:

    A.在声明的时刻,string存储的是什么?

    B.什么原因使得两个string的引用地址是一样的?

  这就引出了我们要讨论的核心问题:字符串驻留.

三.字符串驻留

    1.string存储的是引用

        string工具存储的是引用,引用工具存储在堆中,会天生一个工具,同时将这个工具的地址(引用)给客栈去使用.也就是说两个string引用了堆中统一块工具.

    2.字符串驻留让两个string的引用地址是一样

       CLR初始化时,会建立一个Hash,在这个表中,Key是字符串,值是字符串在堆中的地址.当声明一个字符串的时刻,会先去这个HashTable中去找是否存在这个Key,若是存在则返回对应的引用,若是不存在则纳入HashTable.如下图所示:  

       

   Step1:当执行语句string str1 = "Hello World!";时,str1拿到了Add1;

   Step2:当执行语句string str2= "Hello World!";时,CLR会去HashTable中去找,找到,返回Add1给str2;

   Step3:现在用object.ReferenceEquals对照str1和str2的引用,由于都是Add1,因而返回True.

   我们现在通过内存剖析工具ANTS Memory Profile来证实,字符串驻留机制是确实存在的.

 代码如下:

1 static void Main(string[] args)
2 {
3      Console.ReadLine();//第台?一?次?快ì照?位?置?
4      string str1 = "Hello World!";
5      string str2 = "Hello World!";
6      Console.ReadLine();//第台?二t次?快ì照?位?置?
7 }

    加载两次快照,对比差异:

   

      我们可以看到,在这里有一个string的实例进去了,而且整个历程当中,也只有这一个string实例进去了,我们可以进一步看下进去的内容是什么.

       

        我们在这里发现了”Hello World!”字符串,而且只有一个.这也就从内存剖析的角度证实了字符串驻留的存在.

        3.驻留字符串的HashTable是不受GC治理,但表达式中存在variable,则不驻留在HashTable

            我们实验如下:

 1 static void Main(string[] args)
 2 {
 3      Console.ReadLine();//第1次快照位置
 4      Test();
 5      GC.Collect();
 6      Console.ReadLine();//第3次快照位置
 7 }
 8 
 9 static void Test()
10 {
11      string str1 = "Hello World!";
12      string str2 = "Hello World!" + str1;
13 Console.ReadLine();//第2次快照位置
14 }

       2次快照,我们可以看到:

       

      进去了3个工具,分别是:byteIndex,”Hello World!”,”Hello World!Hello World!”

     

         3次快照是在调用了GC.Collect()后再举行的快照,以快照2为对比线,我们查看第3次快照.

       

          我们看到,有一个工具被GC接纳掉了,详细是什么被接纳了?我们再看:

         

           现在只剩下byteIndex,”Hello World!”两个工具,什么被接纳了呢?显然是:”Hello World!Hello World!”

     这也就证实了我们所说的:驻留字符串的HashTable是不受GC治理,但表达式中存在variable,则不驻留在HashTable.

     进一步:除非卸载AppDomain或历程终止,否则HashTable引用的string工具不能被释放.

       4.字符串的驻留是基于整个历程的

       我们添加两个差别的AppDomain,在各自的应用程序域中执行BuildString()方式,同时由于应用程序域之间本是不能接见相互工具的,我们使用"封送(Marshaling)"机制,封送又分为按值分送(主要接纳序列化的方式)和按引用封送(如接纳.Net Remoting).这里,要实现按引用封送,Test类继续MarshalByRefObject.

       测试代码

class Program
{
     static void Main(string[] args)
     {
           Console.ReadLine();
            AppDomain domina1 = AppDomain.CreateDomain("First");
            Test t1 = (Test)domina1.CreateInstanceAndUnwrap(typeof(Test).Assembly.FullName, typeof(Test).FullName);
            t1.BuildString();

            AppDomain domina2 = AppDomain.CreateDomain("Second");
            Test t2 = (Test)domina1.CreateInstanceAndUnwrap(typeof(Test).Assembly.FullName, typeof(Test).FullName);
            t2.BuildString();

            Console.ReadLine();

        }
    }

public class Test : MarshalByRefObject
{
     public void BuildString()
     {
          var str1 = "Hello";
          var str2 = "Hello";
          var str3 = "World";
          var str4 = "World";
       }
}

  我们拿到两张快照,在第1张跟第2张快照对比后我们发现:

     

        我们再详细查看内容(“World”字符串就不截图了):

     

     通过以上的剖析,我们确信,字符串的驻留是基于整个历程的.

        5.我们可以通过string.Intern方式来将字符串强制加入HashTable,也可以通过string.IsInterned来判断字符串是否在HashTable中存在。

四.字符串池

    在编译时,编译器会处置所有的literal字符串,并嵌入托管模块的元数据中,但若是每次都写入元数据,假设这个字符串在程序中多次泛起,那就需要多次写入元数据,这会使天生的文件无限地增大.

    C#编译器,只在元数据中将literal字符串写入一次,将多个实例合并成一个实例,所有引用该字符串的代码都被修改成引用元数据中的统一个字符串,这能显著地削减天生文件的巨细.这种特征,我们称之为字符串池.

五.string的不能变性

    string是不能变的,这意味着:

    a.字符串一经建立便不能更改,不能变长、变短或修改其中的任何字符;

    b.每次对于字符串的调换操作,若是是带变量操作,都会在堆上天生新的字符串,并返回新的引用,会造成频仍的GC接纳,从而造成性能问题,若是不带变量操作则会接纳字符串驻留;

    c.操作和接见字符串不会发生线程同步问题,线程平安;

    d.String类是sealed(密封)的,这是为了珍爱string的不能变性。

  问题来了,若何实现string的不能变性呢?

     string在内部是用char数组实现的,在char数据中,我们不能以改变数组的引用,然则我们可以直接修改char数组的值,为了实现string的不能变性,string在实现种种方式时,不会触动char数组中的元素。

  参见7.

六.StringBuilder:为解决string的性能而生

    通过前面的内容我们可以知道,string容易发生性能问题,StringBuilder可以解决这个问题。

    它的内部使用char[]来举行操作,默以为16,若是跨越容量,则在堆中发生一个倍增容易的新char[]数组,复制字符,并开始使用新数组,前一个数组则被GC接纳。若是不跨越当前容量,是不是会发生一个新的char[]数组的。

    使用ToString()方式也会在堆中发生一个新的工具。

七.总结

        1.string是引用类型

     2.string使用了字符串池来削减元数据文件的巨细

     3.string使用了字符串驻留来提升效率,驻留的字符串接纳HashTable来存储,它不受GC统领,HashTable是基于历程共享的.

【关于云返利网】

云返利网是阿里云、腾讯云、华为云产品推广返利平台,在各个品牌云产品官网优惠活动之外,云返利网还提供返利。您可以无门槛获得阿里云、华为云、腾讯云所有产品返利,在官网下单后就可以领取,无论是自己用、公司用还是帮客户采购,您个人都可以获得返利。云返利网的目标是让返利更多、更快、更简单!详情咨询13121395187(微信同号)