现在的位置: 首页 > 综合 > 正文

System.Text.StringBuilder

2014年02月28日 ⁄ 综合 ⁄ 共 24038字 ⁄ 字号 评论关闭
// ==++==
//
//  
//    Copyright (c) 2002 Microsoft Corporation.  All rights reserved.
//  
//    The use and distribution terms for this software are contained in the file
//    named license.txt, which can be found in the root of this distribution.
//    By using this software in any fashion, you are agreeing to be bound by the
//    terms of this license.
//  
//    You must not remove this notice, or any other, from this software.
//  
//
// ==--==
/*============================================================
**
** Class:  StringBuilder
**
**                                       
**
** Purpose: A prototype implementation of the StringBuilder
** class.
**
** Date:  December 8, 1997
** Last Updated:  March 31, 1998
**
===========================================================*/
namespace System.Text {
    using System.Text;
    using System.Runtime.Serialization;
    using System;
    using System.Runtime.CompilerServices;

    // This class represents a mutable string.  It is convenient for situations in
    // which it is desirable to modify a string, perhaps by removing, replacing, or
    // inserting characters, without creating a new String subsequent to
    // each modification.
    //
    // The methods contained within this class do not return a new StringBuilder
    // object unless specified otherwise.  This class may be used in conjunction with the String
    // class to carry out modifications upon strings.
    //
    // When passing null into a constructor in VJ and VC, the null
    // should be explicitly type cast.
    // For Example:
    // StringBuilder sb1 = new StringBuilder((StringBuilder)null);
    // StringBuilder sb2 = new StringBuilder((String)null);
    // Console.WriteLine(sb1);
    // Console.WriteLine(sb2);
    //
    /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder"]/*' />
    [Serializable()] public sealed class StringBuilder {


        //
        //
        //  CLASS VARIABLES
        //
        //
        internal int m_currentThread = InternalGetCurrentThread();
        internal int m_MaxCapacity = 0;
        internal String m_StringValue = null;


        //
        //
        // STATIC CONSTANTS
        //
        //
        internal const int DefaultCapacity = 16;


        //
        //
        //CONSTRUCTORS
        //
        //
   
        // Creates a new empty string builder (i.e., it represents String.Empty)
        // with the default capacity (16 characters).
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder"]/*' />
        public StringBuilder()
            : this(DefaultCapacity) {
        }
       
        // Create a new empty string builder (i.e., it represents String.Empty)
        // with the specified capacity.
/// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder1"]/*' />
        public StringBuilder(int capacity) {
            if (capacity<0) {
                throw new ArgumentOutOfRangeException("capacity",
                                                      String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
            }

            if (capacity == 0) { // MakeFromString enforces this
                capacity = DefaultCapacity;
            }

            m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity);
            m_MaxCapacity = Int32.MaxValue;
        }
     
   
        // Creates a new string builder from the specified string.  If value
        // is a null String (i.e., if it represents String.NullString)
        // then the new string builder will also be null (i.e., it will also represent
        //  String.NullString).
        //
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder2"]/*' />
        public StringBuilder(String value){
            MakeFromString(value, 0, -1, -1);
        }
   
        // Creates a new string builder from the specified string with the specified
        // capacity.  If value is a null String (i.e., if it represents
        // String.NullString) then the new string builder will also be null
        // (i.e., it will also represent String.NullString).
        // The maximum number of characters this string may contain is set by capacity.
        //
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder3"]/*' />
        public StringBuilder(String value, int capacity) {
            if (capacity<0) {
                throw new ArgumentOutOfRangeException("capacity",
                                                      String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
            }

            MakeFromString(value, 0, -1, capacity);
        }
        // Creates a new string builder from the specifed substring with the specified
        // capacity.  The maximum number of characters is set by capacity.
        //
       
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder4"]/*' />
        public StringBuilder(String value, int startIndex, int length, int capacity) {
            if (capacity<0) {
                throw new ArgumentOutOfRangeException("capacity",
                                                      String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
            }
            if (length<0) {
                throw new ArgumentOutOfRangeException("length",
                                                      String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBeNonNegNum"), "length"));
            }

            MakeFromString(value, startIndex, length, capacity);
        }
   
        // Creates an empty StringBuilder with a minimum capacity of capacity
        // and a maximum capacity of maxCapacity.
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.StringBuilder5"]/*' />
        public StringBuilder(int capacity, int maxCapacity) {
            if (capacity>maxCapacity) {
                throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_Capacity"));
            }
            if (maxCapacity<1) {
                throw new ArgumentOutOfRangeException("maxCapacity", Environment.GetResourceString("ArgumentOutOfRange_SmallMaxCapacity"));
            }
   
            if (capacity<0) {
                throw new ArgumentOutOfRangeException("capacity",
                                                      String.Format(Environment.GetResourceString("ArgumentOutOfRange_MustBePositive"), "capacity"));
            }
            if (capacity == 0) {
                capacity = DefaultCapacity;
            }
               
            m_StringValue = String.GetStringForStringBuilder(String.Empty, capacity);
            m_MaxCapacity = maxCapacity;

        }
   
        private String GetThreadSafeString(out int tid) {
             String temp = m_StringValue;
             tid = InternalGetCurrentThread();
             if (m_currentThread == tid)
                return temp;
             return String.GetStringForStringBuilder(temp, temp.Capacity);
        }

        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern static int InternalGetCurrentThread();
       
        //
        // Private native functions
        //
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        private extern void MakeFromString(String value, int startIndex, int length, int capacity);

         /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Capacity"]/*' />
         public int Capacity {
             get {return m_StringValue.Capacity;} //-1 to account for terminating null.
             set {InternalSetCapacity(value);}
        }

   
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.MaxCapacity"]/*' />
        public int MaxCapacity {
            get { return m_MaxCapacity; }

        }
   
        // Read-Only Property
        // Ensures that the capacity of this string builder is at least the specified value. 
        // If capacity is greater than the capacity of this string builder, then the capacity
        // is set to capacity; otherwise the capacity is unchanged.
        //
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.EnsureCapacity"]/*' />
        public int EnsureCapacity(int capacity) {
            if (capacity<0) {
                throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_NeedPosCapacity"));
            }

            int tid;
            String currentString =  GetThreadSafeString(out tid);

            //If we need more space or the COW bit is set, copy the buffer.
            if (!NeedsAllocation(currentString,capacity)) {
                return currentString.Capacity;
            }

            String newString = GetNewString(currentString,capacity);
            ReplaceString(tid,newString);
            return newString.Capacity;
        }
   
        //Sets the capacity to be capacity.  If capacity is less than the current
        //instance an ArgumentException is thrown.  If capacity is greater than the current
        //instance, memory is allocated to allow the StringBuilder to grow.
        //
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        internal extern int InternalSetCapacity(int capacity);


        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.ToString"]/*' />
        public override String ToString() {
            String currentString =  m_StringValue;
            int currentThread = m_currentThread;
            if (currentThread != 0 && currentThread != InternalGetCurrentThread()) {
                return String.InternalCopy(currentString);
            }

            if ((2 *  currentString.Length) <  currentString.ArrayLength) {
                return String.InternalCopy(currentString);
            }

            currentString.ClearPostNullChar();
            m_currentThread = 0;
            return  currentString;
        }
   
        // Converts a substring of this string builder to a String.
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.ToString1"]/*' />
        public String ToString(int startIndex, int length) {
            return  m_StringValue.Substring(startIndex, length);
        }
   
        // Sets the length of the String in this buffer.  If length is less than the current
        // instance, the StringBuilder is truncated.  If length is greater than the current
        // instance, nulls are appended.  The capacity is adjusted to be the same as the length.
       
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Length"]/*' />
        public int Length {
            get {
               return  m_StringValue.Length;
            }
            set {
                int tid;
                String currentString =  GetThreadSafeString(out tid);

                if (value==0) { //the user is trying to clear the string
                     currentString.SetLength(0);
                     ReplaceString(tid,currentString);
                     return;
                }
                            
                int currentLength = currentString.Length;
                int newlength = value;
                //If our length is less than 0 or greater than our Maximum capacity, bail.
                if (newlength<0) {
                    throw new ArgumentOutOfRangeException("newlength", Environment.GetResourceString("ArgumentOutOfRange_NegativeLength"));
                }

                if (newlength>MaxCapacity) {
                    throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_SmallCapacity"));
                }

                //Jump out early if our requested length our currentlength.
                //This will be a pretty rare branch.
                if (newlength == currentLength) {
                    return;
                }

           
                //If the StringBuilder has never been converted to a string, simply set the length
                //without allocating a new string.
                if (newlength <= currentString.Capacity) {
                        if (newlength > currentLength) {
                            for (int i = currentLength ; i < newlength; i++) // This is a rare case anyway.
                                currentString.InternalSetCharNoBoundsCheck(i,'/0');
                        }

                        currentString.InternalSetCharNoBoundsCheck(newlength,'/0'); //Null terminate.
                        currentString.SetLength(newlength);
                        ReplaceString(tid,currentString);
                  
                        return;
                }

                // CopyOnWrite set we need to allocate a String
                int newCapacity = (newlength>currentString.Capacity)?newlength:currentString.Capacity;
                String newString = String.GetStringForStringBuilder(currentString, newCapacity);
           
                //We know exactly how many characters we need, so embed that knowledge in the String.
                newString.SetLength(newlength);
                ReplaceString(tid,newString);
            }
        }
   
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.this"]/*' />
        [System.Runtime.CompilerServices.IndexerName("Chars")]
        public char this[int index] {
            get {
                return  m_StringValue[index];
            }
            set {
                int tid;
                String currentString =  GetThreadSafeString(out tid);
                currentString.SetChar(index, value);
                ReplaceString(tid,currentString);
            }
        }

        // Appends a character at the end of this string builder. The capacity is adjusted as needed.
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append"]/*' />
        public StringBuilder Append(char value, int repeatCount) {
            if (repeatCount==0) {
                return this;
            }
            if (repeatCount<0) {
                throw new ArgumentOutOfRangeException("repeatCount", Environment.GetResourceString("ArgumentOutOfRange_NegativeCount"));
            }

            
            int tid;
            String currentString =  GetThreadSafeString(out tid);
           
            int currentLength = currentString.Length;
            int requiredLength = currentLength + repeatCount;

            if (requiredLength < 0)
                throw new OutOfMemoryException();

            if (!NeedsAllocation(currentString,requiredLength)) {
                currentString.AppendInPlace(value, repeatCount,currentLength);
                ReplaceString(tid,currentString);
                return this;
            }

            String newString = GetNewString(currentString,requiredLength);
            newString.AppendInPlace(value, repeatCount,currentLength);
            ReplaceString(tid,newString);
            return this;
        }
   
        // Appends an array of characters at the end of this string builder. The capacity is adjusted as needed.
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append1"]/*' />
        public StringBuilder Append(char[] value, int startIndex, int charCount) {
            int requiredLength;

            if (value==null) {
                if (startIndex==0 && charCount==0) {
                    return this;
                }
                throw new ArgumentNullException("value");
            }

            if (charCount==0) {
                return this;
            }

            if (startIndex<0) {
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
            }
            if (charCount<0) {
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
            }
            if (charCount>value.Length-startIndex) {
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_Index"));
            }
            
            int tid;
            String currentString =  GetThreadSafeString(out tid);
           
            int currentLength = currentString.Length;
            requiredLength = currentLength + charCount;
            if (NeedsAllocation(currentString,requiredLength)) {
                String newString = GetNewString(currentString,requiredLength);
                newString.AppendInPlace(value, startIndex, charCount,currentLength);
                ReplaceString(tid,newString);
            } else {
                currentString.AppendInPlace(value, startIndex, charCount,currentLength);
                ReplaceString(tid,currentString);
            }

            return this;
        }
   
        // Appends a copy of this string at the end of this string builder.
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append2"]/*' />
        public StringBuilder Append(String value) {
            //If the value being added is null, eat the null
            //and return.
            if (value==null) {
                return this;
            }

            int tid;
            // hand inlining of GetThreadSafeString
            String currentString = m_StringValue;
            tid = InternalGetCurrentThread();
            if (m_currentThread != tid)
                currentString = String.GetStringForStringBuilder(currentString, currentString.Capacity);

            int currentLength = currentString.Length;
         
            int requiredLength = currentLength + value.Length;
           
            if (NeedsAllocation(currentString,requiredLength)) {
                String newString = GetNewString(currentString,requiredLength);
                newString.AppendInPlace(value,currentLength);
                ReplaceString(tid,newString);
            } else {
                currentString.AppendInPlace(value,currentLength);
                ReplaceString(tid,currentString);
            }

            return this;
        }

        internal unsafe StringBuilder Append(char *value, int count) {
            //If the value being added is null, eat the null
            //and return.
            if (value==null) {
                return this;
            }

            
            int tid;
            String currentString =  GetThreadSafeString(out tid);
            int currentLength = currentString.Length;
           
            int requiredLength = currentLength + count;
           
            if (NeedsAllocation(currentString,requiredLength)) {
                String newString = GetNewString(currentString,requiredLength);
                newString.AppendInPlace(value, count,currentLength);
                ReplaceString(tid,newString);
            } else {
                currentString.AppendInPlace(value,count,currentLength);
                ReplaceString(tid,currentString);
            }

            return this;
        }

        private bool NeedsAllocation(String currentString,int requiredLength) {
            //<= accounts for the terminating 0 which we require on strings.
            return (currentString.ArrayLength<=requiredLength);
        }

        private String GetNewString(String currentString, int requiredLength) {
            int newCapacity;

            requiredLength++; //Include the terminating null.

            if (requiredLength >  m_MaxCapacity) {
                throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"),
                                                      "requiredLength");
            }
         
            newCapacity = ( currentString.Capacity)*2; // To force a predicatable growth of 160,320 etc. for testing purposes

            if (newCapacity<requiredLength) {
                newCapacity = requiredLength;
            }

            if (newCapacity> m_MaxCapacity) {
                newCapacity =  m_MaxCapacity;
            }

            if (newCapacity<=0) {
                throw new ArgumentOutOfRangeException(Environment.GetResourceString("ArgumentOutOfRange_NegativeCapacity"));
            }

            return String.GetStringForStringBuilder( currentString, newCapacity);
        }

        private void ReplaceString(int tid, String value) {
            BCLDebug.Assert(value!=null, "[StringBuilder.ReplaceString]value!=null");
           
            m_currentThread = tid; // new owner
            m_StringValue = value;
        }
   
        // Appends a copy of the characters in value from startIndex to startIndex +
        // count at the end of this string builder.
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append3"]/*' />
        public StringBuilder Append (String value, int startIndex, int count) {
            //If the value being added is null, eat the null
            //and return.
            if (value==null) {
                if (startIndex==0 && count==0) {
                    return this;
                }
                throw new ArgumentNullException("value");
            }

            if (count<=0) {
                if (count==0) {
                    return this;
                }
                throw new ArgumentOutOfRangeException("count", Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
            }

            if (startIndex<0 || (startIndex>value.Length - count)) {
                throw new ArgumentOutOfRangeException("startIndex", Environment.GetResourceString("ArgumentOutOfRange_Index"));
            }

            int tid;
            String currentString =  GetThreadSafeString(out tid);
            int currentLength = currentString.Length;
     
            int requiredLength = currentLength + count;
           
            if (NeedsAllocation(currentString,requiredLength)) {
                String newString = GetNewString(currentString,requiredLength);
                newString.AppendInPlace(value, startIndex, count, currentLength);
                ReplaceString(tid,newString);
            } else {
                currentString.AppendInPlace(value, startIndex, count, currentLength);
                ReplaceString(tid,currentString);
            }

            return this;
        }
     
        // Inserts multiple copies of a string into this string builder at the specified position.
        // Existing characters are shifted to make room for the new text.
        // The capacity is adjusted as needed. If value equals String.Empty, this
        // string builder is not changed.
        //
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Insert"]/*' />
        public unsafe StringBuilder Insert(int index, String value, int count) {
              int tid;
              String currentString =  GetThreadSafeString(out tid);
              int currentLength = currentString.Length;
             
              //If value isn't null, get all of our required values.
              if (value == null) {
                    if (index == 0 && count == 0) {
                         return this;
                    }
                    throw new ArgumentNullException(Environment.GetResourceString("ArgumentNull_String"));
              }

              //Range check the index.
              if (index < 0 || index > currentLength) {
                  throw new ArgumentOutOfRangeException("index",Environment.GetResourceString("ArgumentOutOfRange_Index"));
              }

              if (count < 1) {
                  throw new ArgumentOutOfRangeException("count",Environment.GetResourceString("ArgumentOutOfRange_GenericPositive"));
              }

               //Calculate the new length, ensure that we have the space and set the space variable for this buffer
               int requiredLength;
                try {
                   requiredLength = checked(currentLength + (value.Length * count));
                }
                catch (Exception) {
                   throw new OutOfMemoryException();
                }

               if (NeedsAllocation(currentString,requiredLength)) {
                    String newString = GetNewString(currentString,requiredLength);
                    newString.InsertInPlace(index, value, count, currentLength, requiredLength);
                    ReplaceString(tid,newString);
               }
               else {
                    currentString.InsertInPlace(index, value, count, currentLength, requiredLength);
                    ReplaceString(tid,currentString);
               }
               return this;
         }

      
   
        // Property.
        // Removes the specified characters from this string builder.
        // The length of this string builder is reduced by
        // length, but the capacity is unaffected.
        //
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Remove"]/*' />
        [MethodImplAttribute(MethodImplOptions.InternalCall)]
        public extern StringBuilder Remove(int startIndex, int length);
           
        //
        //
        // PUBLIC INSTANCE FUNCTIONS
        //
        //
   
        /*====================================Append====================================
        **
        ==============================================================================*/
        // Appends a boolean to the end of this string builder.
        // The capacity is adjusted as needed.
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append4"]/*' />
        public StringBuilder Append(bool value) {
            return Append(value.ToString());
        }
     
        // Appends an sbyte to this string builder.
        // The capacity is adjusted as needed.
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append5"]/*' />
        [CLSCompliant(false)]
        public StringBuilder Append(sbyte value) {
            return Append(value.ToString());
        }
   
        // Appends a ubyte to this string builder.
        // The capacity is adjusted as needed.
        /// <include file='doc/StringBuilder.uex' path='docs/doc[@for="StringBuilder.Append6"]/*' />
        public StringBuilder A

抱歉!评论已关闭.