string:你真的理解正确String某些特性了吗?来源: 发布时间:星期日, 2009年9月13日 浏览:18次 评论:0
最近在园子里看到几篇有关文章,感觉其中有些误解,不知道是自己理解有误还是园友理解,也没发现有园友提出质疑,索性也将自己点理解写出来,也对些质疑提出了自己解释,不管怎样我希望如果是我哪里理解大家定要提出来,我们起进步,否则真会误导很多人,也感觉到写文章是要负责任,否则就干脆写日志,不要发表出来误导到了些对.Net不熟悉朋友!
1.在DoNet中String是不可改变,什么叫不可变呢,首先大家先看下面例子: a = "1"; Console.WriteLine(a);//1 a"2"; Console.WriteLine(a);//12 大家可以看看下面生成对应IL代码,可以看到a"2"并非是在原有分配堆中进行修改, 而是创建了个新串; 而+在IL中也是使用Concat,将两个串联; 所以String不可变就是当你在创建了串后其不可修改; .method private hidebysig void Main( args) cil managed { .entrypo // 代码大小 34 (0x22) .maxstack 2 .locals init ([0] a) IL_0000: nop IL_0001: ldstr "1" IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: call void [mscorlib].Console::WriteLine() IL_000d: nop IL_000e: ldloc.0 IL_000f: ldstr "2" IL_0014: call [mscorlib].String::Concat(, ) IL_0019: stloc.0 IL_001a: ldloc.0 IL_001b: call void [mscorlib].Console::WriteLine() IL_0020: nop IL_0021: ret } // end of method Program::Main 2.当创建多个串内容相同对象时,都只会指向同个引用; a = "11"; b = "11"; c = "11"; d = "1"; d "1"; a和b都指向同个a引用,并不会为b重新分配内存;这样即可保证内存有效利用;这点用IL不是很好看出来,所以大家可以按下面思路方法去比较他们引用地址,都是指向同个堆; Console.WriteLine(.ReferenceEquals(a, b)); Console.WriteLine(.ReferenceEquals(a, c)); Console.WriteLine(.ReferenceEquals(c, b)); Console.WriteLine(.ReferenceEquals(a, d)); Console.WriteLine(.ReferenceEquals(b, d)); Console.WriteLine(.ReferenceEquals(c, d)); 运行结果: True True True False False False 这介绍说明当我们新建个串以后,将为其分配内存,而的后如何有值和其相同串不会被创建分配堆而是直接分配其引用; 3.串比较: 和!=是为了比较String对象值是否相同而不是比较引用; 例如等号首先是比较引用是否相同,引用相同值定会相同,就避免了再去对值进行比较; 而引用如果区别再去比较值,相同则返回true; 总的定定是为了比较串值是否相同; s2 = StringBuilder.Append("My").Append("Test").; s3 = StringBuilder.Append("My").Append("Test").; Console.WriteLine((object)s2(object)s3);//False 上面是返回False,大家肯定有些迷惑,而此前位园友则解释我也不尽赞同,这里只发表我解释: 此处比较是两个引用类型,为什么这么说,大家都知道引用地址都是存放在栈中,而此处正是将存放在栈中引用地址装箱转换成了引用类型也就是分别创建了两个值为引用地址堆,而他们引用地址值肯定不样,所以结果显然就是False了; 4.为什么总是提倡使用StringBuilder对象; 如果用String,每进行次串拼接就要分配个新堆,如果只有两次倒无所谓,如果频繁如此操作性能会很差;但StringBuilder是在为其分配堆上做修改,不会重新分配,所以性能比String拼接串好;相信大家理解上面几点了我这里也纯粹是废话了,呵呵! 5.最后就是看到有人提到Intern思路方法,我感觉他们对此思路方法具体功能理解不透彻存在误解; 首先看下面例子: void Main( args) { a = "12"; b = "1"; b "2"; c = .Intern(b); Console.WriteLine((object)a (object)b);//false Console.WriteLine((object)a (object)c);//true Console.WriteLine((object)b (object)c);//false } void Main( args) { a = "1212"; b = "1"; b "2"; c = .Intern(b); Console.WriteLine((object)a (object)b);//false Console.WriteLine((object)a (object)c);//false Console.WriteLine((object)b (object)c);//true } void Main( args) { b = "1"; b "2"; c = .Intern("12"); Console.WriteLine((object)b (object)c);//false } 先看第个Main,我根据第个Main来解释: 如果传递给Intern是个变量引用地址,那么他会检索是否用和此引用地址所指向堆值相同其他堆,如果有他会返回此堆引用地址,也就是a 我们再来看第 2个Main来继续解释: 如果没有找到和所指向堆值相同其他堆,他将返回b引用地址; 最后看第 3个Main做最后解释: 如果传递进去是串而不是引用地址,他样会检索是否存在和此串相同堆,如果有则返回此堆引用地址,如无则驻留此串并返回其引用地址; 而这里所将驻留也是String特殊的处,串在被创建后不会被立即回收,即使已不存在对其引用;他会在被创建的后直驻留在内存中直至结束,大家可以查看MSDN解释,下面将拿出些MSDN解释来配合介绍说明下; 其中=>符号是我做注释: ------MSDN解释--Start------------------------------------------------------------------------------------ 在1.1中 str1 = String.Empty; str2 = String.Intern(String.Empty); ((object) str1) ((object) str2); ... >是返回false,但在2.0中返回true,所以更加证实了如上所说;下面主要是讲解了String拘留池概念,大家理解上面概念后相信理解他也不成问题; 公共语言运行库通过维护个表来存放串,该表称为拘留池,它包含中以编程方式声明或创建每个唯串个引用.因此,具有特定值串例子在系统中只有个.例如,如果将同串分配给几个变量,运行库就会从拘留池中检索对该串相同引用,并将它分配给各个变量.Intern 思路方法使用拘留池来搜索和 str 值相等串.如果存在这样串,则返回拘留池中它引用 如果不存在,则向拘留池添加对 str 引用,然后返回该引用.如果要减少应用分配内存总量,请记住留用串有两个不希望出现副作用:首先,为留用 String 对象分配内存在公共语言运行库 (CLR) 终止的前不大可能释放.这是 CLR 对留用 String 对象引用可能保持到应用终止的后,甚至可能保持到应用域终止的后.其次,要留用串,必须先创建串.即使 String 对象使用内存最终将通过垃圾回收,仍然必须分配该内存. 上面就说到了[为留用 String 对象分配内存在公共语言运行库 (CLR) 终止的前不大可能释放.] .NET Framework 2.0 版引入了 CompilationRelaxations.NoStringInterning 枚举成员. NoStringInterning 成员将集标记为不需要串拘留.可以使用 CompilationRelaxationsAttribute 属性将NoStringInterning 应用于某个集.使用本机映像生成器 (Ngen.exe) 将该集安装到本地计算机上本机映像缓存Cache时,不使用串拘留.这里所提到本机映像可以改善内存使用情况,大家可以查MSDN详细了解; 源自:51cto.com 0
相关文章读者评论发表评论 |