c#基础知识

1.string与StringBuilder

   string 是不可变的,是引用类型继承与Object(值类型继承于ValueType),每次拼接string其实是在托管堆上构造一个新的对象。这样在反复的拼接字符串的时候就会产生大量的垃圾字符串,由GC自动回收,这个时候GC会频繁的回收垃圾字符串,影响性能,所以通常推荐使用StringBuilder来做字符串拼接,

        那么string支持字符串拼接的意义何在呢?

         这里就得说到字符串池这个概念,当我们创建一个字符串的时候,会先去字符串池中找(哈希表),如果没有找到就会创建一个新的字符串并保存到字符串池中,当创建的字符串已经在字符串池中存在的时候,就会去引用。这样已经创建过的字符串就可以共享,节省空间。因为在程序中,我们会使用大量的字符串,假如我们每次出现的字符串都在内存空间中开辟一块空间来存储,就会在内存中产生大量相同的字符串,也会造成垃圾回收频繁的执行,影响性能。所以string的字符串拼接适合少量的拼接,对于大量的拼接,则推荐使用StringBuilder。

      下面说说string的字符串拼接与StringBuilder拼接的简单过程。

 string的字符串拼接过程:string a='A'; a+=B;

1.开辟足够大的临时存储空间来保证足够存储字符串A和B。

2.将A复制到临时区的开始处。

3.将B复制到临时存储区的结尾处。

4.释放a的旧有内存。

5.给a分配新内存。

6.将临时存储空间的字符串复制到5中新开辟的内存,并将a的引用指向新内存。

  StringBuilder拼接的过程:StringBuilder是链式存储结构,构造函数初始化StringBuilder实例的大小,可存储的最大容量,当前字符串。当新添加的字符串大于当前StringBuilder剩余空间的时候,StringBuilder就会开辟出一块新的空间(大小=原大小*2),新字符串补充满剩余内存空间后,将剩下的字符串放入新开辟的内存空间。引用一下网上的一张图。

 2者之间的本质区别在于,string在字符串拼接的时候是创建一个新的对象来保存拼接后的字符串,而StringBuilder则是在原StringBuilder对象上开辟新的内存空间,是操作原对象而非string的创建新对象。

2.值类型一定在栈上吗?

  我们都知道值类型是存放在线程栈上面,但其实只有值类型作为临时变量的时候才存放在栈上,作为成员变量,存储在堆中。

1.引用类型的内部变量,即使是值类型,也会在引用类型被实例是一起存放在堆上,方法内部的值类型变量不会初始化,在执行方法的时候放在栈上面。

2.对于值类型的数组,因为数组是引用类型。所以数组内的值类型也在堆上。

3.闭包,lamda表达式。如下:           

     Action<int> act = a =>{  Console.WriteLine(a); };

C# 成的IL 会添加一个静态的辅助类,闭包内的局部变量 也会成为辅助类的成员变量,因此,这种值类型的局部变量也被分配到堆上。

闭包的陷阱:参考 菩提树下的杨过 :http://www.cnblogs.com/yjmyzz/archive/2009/03/13/1410924.html

using System;
using System.Collections.Generic;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            List<Action> ls = new List<Action>();
            for (int i = 0; i < 10; i++)
            {                
                ls.Add(() => Console.WriteLine(i));                
            }

            foreach (Action action in ls)
            {
                action();
            }
            System.Console.Read();
        }       
    }  

}

结果:一连输出了10行完全相同的"10"(可能并没有按代码编写者的"意图",输出0到9)

看了链接里面其实是编译生成了一个类,类里面有一个变量i和打印i的方法,然后创建一个Action委托集合将方法赋值,最后遍历集合执行,在执行的时候i已经加到10了。所以要通过一个内部变量来避免这种陷阱。。

3.哈希表的实现方法?

   哈希表 hashtable ,也称散列表,主要是根据关键码值(key,value)来直接访问数据结构,通过把关键码值映射到表中一个位置来访问记录。

  它的实现方法是:把key通过一个固定的算法函数(除余法,数字选择法)来转换成一个整数字符,然后将该整数除以一个数字进行取余,将余数作为数组的下标,将value存储在该余数的数组下标的空间内。当使用哈希表查询的时候,再次将key通过相同函数转换整数取余得到下标,定位到对应的数组控件获取Value值。(通过余数分配不同的数组空间,查询的时候通过余数找对应的数组空间,这样就避免了全表扫描,节约时间。)

a) 除余法: 
       选择一个适当的正整数 p ,令 h(k ) = k mod p ,这里, p 如果选取的是比较大的素数,效果比较好。而且此法非常容易实现,因此是最常用的方法。 
b) 数字选择法: 
       如果关键字的位数比较多,超过长整型范围而无法直接运算,可以选择其中数字分布比较均匀的若干位,所组成的新的值作为关键字或者直接作为函数值。 

             

  

    

原文地址:https://www.cnblogs.com/m7777/p/4106393.html