深入理解string和如何高效地使用string

2019-03-16 22:00

深入理解string和如何高效地使用string

无论你所使用的是哪种编程语言,我们都不得不承认这样一个共识:string是我们使用最为频繁的一种对象。但是string的常用性并不意味着它的简单性,而且我认为,正是由于string的频繁使用才会促使其设计人员在string的设计上花大量的功夫。所以正是这种你天天见面的string,蕴含了很多精妙的设计思想。

一个月以前我写了一篇讨论字符串的驻留(string interning)的文章,我今天将会以字符串的驻留为基础,进一步来讨论.NET中的string。string interning的基本前提是string的恒定性(immutability),即string一旦被创建将不会改变。我们就先来谈谈string的恒定性。

一、 string是恒定的(immutable)

和其他类型比较,string最为显著的一个特点就是它具有恒定不变性:我们一旦创建了一个string,在managed heap 上为他分配了一块连续的内存空间,我们将不能以任何方式对这个string进行修改使之变长、变短、改变格式。所有对这个string进行各项操作(比如调用ToUpper获得大写格式的string)而返回的string,实际上另一个重新创建的string,其本身并不会产生任何变化。

String的恒定性具有很多的好处,它首先保证了对于一个既定string的任意操作不会造成对其的改变,同时还意味着我们不用考虑操作string时候出现的线程同步的问题。在string恒定的这些好处之中,我觉得最大的好处是:它成就了字符串的驻留。

CLR通过一个内部的interning table保证了CLR只维护具有不同字符序列的string,任何具有相同字符序列的string所引用的均为同一个string对象,同一段为该string配分的内存快。字符串的驻留极大地较低了程序执行对内存的占用。

对于string的恒定性和字符串的驻留,还有一点需要特别指出的是:string的恒定性不单单是针对某一个单独的AppDomain,而是针对一个进程的。

二、 String可以跨AppDomain共享的(cross-appDomain)

我们知道,在一个托管的环境下,Appdomain是托管程序运行的一个基本单元。AppDomain为托管程序提供了良好的隔离机制,保证在同一个进程中的不同的Appdomain不可以共享相同的内存空间。在一个Appdomain创建的对象不能被另一个Appdomain直接使用,对象在AppDomain之间传递需要有一个Marshaling的过程:对象需要通过by reference或者by value的方式从一个Appdomain传递到另一个Appdomain。具体内容可以参照我的另一篇文章:用Coding证明Appdomain的隔离性。

但是这里有一个特例,那就是string。Appdomain的隔离机制是为了防止一个Application的对内存空间的操作对另一个Application 内存空间的破坏。通过前面的介绍,我们已经知道了string是恒定不变的、是只读的。所以它根本不需要Appdomain的隔离机制。所以让一个恒定的、只读的string被同处于一个进程的各个Application共享是没有任何问题的。

String的这种跨AppDomain的恒定性成就了基于进程的字符串驻留:一个进程中各个Application使用的具有相同字符序列的string都是对同一段内存的引用。我们将在下面通过一个Sample来证明这一点。

三、 证明string垮AppDomain的恒定性

在写这篇文章的时候,我对如何证明string跨AppDomain的interning,想了好几天,直到我偶然地想到了为实现线程同步的lock机制。

我们知道在一个多线程的环境下,为了避免并发操作导致的数据的不一致性,我们需要对一个对象加锁来阻止该对象被另一个线程 操作。相反地,为了证明两个对象是否引用的同一个对象,我们只需要在两个线程中分别对他们加锁,如果程序执行的效果和对同一个对象加锁的情况完全一样的话,那么就可以证明这两个被加锁的对象是同一个对象。基于这样的原理我们来看看我们的Sample:

using System;

using System.Collections.Generic; using System.Text; using System.Threading;

namespace Artech.ImmutableString {

class Program {

static void Main(string[] args) {

AppDomain appDomain1 = AppDomain.CreateDomain(\ AppDomain appDomain2 = AppDomain.CreateDomain(\

MarshalByRefType marshalByRefObj1 = appDomain1.CreateInstanceAndUnwrap(\as MarshalByRefType;

MarshalByRefType marshalByRefObj2 = appDomain2.CreateInstanceAndUnwrap(\as MarshalByRefType;

marshalByRefObj1.StringLockHelper = \ marshalByRefObj2.StringLockHelper = \

Thread thread1 = new Thread(new ParameterizedThreadStart(Execute)); Thread thread2 = new Thread(new ParameterizedThreadStart(Execute));

thread1.Start(marshalByRefObj1); thread2.Start(marshalByRefObj2);

Console.Read(); }

static void Execute(object obj) {

MarshalByRefType marshalByRefObj = obj as MarshalByRefType; marshalByRefObj.ExecuteWithStringLocked(); } }

class MarshalByRefType : MarshalByRefObject {

#region Private Fields

private string _stringLockHelper; private object _objectLockHelper; #endregion

#region Public Properties public string StringLockHelper {

get { return _stringLockHelper; } set { _stringLockHelper = value; } }

public object ObjectLockHelper {

get { return _objectLockHelper; } set { _objectLockHelper = value; } }

#endregion

#region Public Methods

public void ExecuteWithStringLocked() {

lock (this._stringLockHelper) {

Console.WriteLine(\ain:\\t{0}\\n\\tTime:\\t\\t{1}\

AppDomain.CurrentDomain.FriendlyName, DateTime.Now); Thread.Sleep(10000); } }

public void ExecuteWithObjectLocked() {

lock (this._objectLockHelper) {

Console.WriteLine(\main:\\t{0}\\n\\tTime:\\t\\t{1}\

AppDomain.CurrentDomain.FriendlyName, DateTime.Now); Thread.Sleep(10000); } }

#endregion } }

我们来简单地分析一下上面的coding.

我们创建了一个继承自MarshalByRefObject,因为我需要让它具有跨AppDomain传递的能力。在这个Class中定义了两个为实现线程同步的helper字段,一个是string类型的_stringLockHelper和object类型的_objectLockHelper,并为他们定义了相应的Property。此外


深入理解string和如何高效地使用string.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:创造性人格的培养方法

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: