2010-11-12 7 views
15

मैं .NET के शुरुआती दिनों से स्पष्ट रूप से याद करता हूं जो स्ट्रिंगबिल्डर द्वारा उपयोग किए जाने वाले आंतरिक चार बफर के साथ नई स्ट्रिंग ऑब्जेक्ट (लौटाया जाना) प्रदान करने के लिए प्रयुक्त होता है। इस तरह यदि आपने स्ट्रिंगबिल्डर का उपयोग करके एक विशाल स्ट्रिंग का निर्माण किया है, तो ToString को कॉल करने की आवश्यकता नहीं है।क्या स्ट्रिंगबिल्डर टूस्टिंग को कॉल करने के बाद अपरिवर्तनीय हो जाता है?

ऐसा करने में, स्ट्रिंगबिल्डर को बफर में किसी भी अतिरिक्त परिवर्तन को रोकना पड़ा, क्योंकि अब इसे एक अपरिवर्तनीय स्ट्रिंग द्वारा उपयोग किया गया था। नतीजतन, स्ट्रिंगबिल्डर एक "कॉपी-ऑन-चेंज" पर स्विच करेगा जहां कोई भी प्रयास किया गया परिवर्तन पहले एक नया बफर बनायेगा, पुराने बफर की सामग्री को कॉपी करें और केवल तभी इसे बदलें।

मुझे लगता है कि धारणा यह थी कि स्ट्रिंगबिल्डर का उपयोग स्ट्रिंग बनाने के लिए किया जाएगा, फिर नियमित स्ट्रिंग में परिवर्तित किया जाएगा और त्याग दिया जाएगा। मुझे एक उचित धारणा की तरह लगता है।

अब यह बात है। मुझे दस्तावेज़ीकरण में इसका कोई उल्लेख नहीं मिल रहा है। लेकिन मुझे यकीन नहीं है कि यह कभी दस्तावेज किया गया था।

तो मैं परावर्तक (.NET 4.0) का उपयोग कर ToString के कार्यान्वयन को देखा है, और मुझे लगता है कि यह वास्तव में प्रतियां स्ट्रिंग, बस बफर का हिस्सा बजाय: अब

[SecuritySafeCritical] 
public override unsafe string ToString() 
{ 
    string str = string.FastAllocateString(this.Length); 
    StringBuilder chunkPrevious = this; 
    fixed (char* str2 = ((char*) str)) 
    { 
     char* chPtr = str2; 
     do 
     { 
      if (chunkPrevious.m_ChunkLength > 0) 
      { 
       char[] chunkChars = chunkPrevious.m_ChunkChars; 
       int chunkOffset = chunkPrevious.m_ChunkOffset; 
       int chunkLength = chunkPrevious.m_ChunkLength; 
       if ((((ulong) (chunkLength + chunkOffset)) > str.Length) ||  (chunkLength > chunkChars.Length)) 
       { 
        throw new ArgumentOutOfRangeException("chunkLength",  Environment.GetResourceString("ArgumentOutOfRange_Index")); 
       } 
       fixed (char* chRef = chunkChars) 
       { 
        string.wstrcpy(chPtr + chunkOffset, chRef, chunkLength); 
       } 
      } 
      chunkPrevious = chunkPrevious.m_ChunkPrevious; 
     } 
     while (chunkPrevious != null); 
    } 
    return str; 
} 

, जैसा कि मैंने इससे पहले कि मैं स्पष्ट रूप से पढ़ना याद रखूं कि शुरुआती दिनों में यह मामला था। नेट। मुझे इस book में भी उल्लेख किया गया।

मेरा सवाल है, क्या यह व्यवहार गिरा दिया गया था? यदि हां, तो कोई जानता है क्यों? यह मेरे लिए सही मायने रखता है ...

+0

दिलचस्प। स्ट्रिंग को char [] s की श्रृंखला के रूप में संग्रहीत किया जाता है। लेकिन रेखा "chunkPrevious = chunkPrevious.m_Chunk पूर्व नहीं है;" मतलब है कि उन सरणी स्ट्रिंगबिल्डर के अलग-अलग उदाहरणों में संग्रहीत हैं, जो एक लिंक-सूची के रूप में संबंधित हैं, आंतरिक रूप से स्ट्रिंगबिल्डर के उदाहरण में हमारे पास संदर्भ है? – Sorax

उत्तर

5

यूप, यह पूरी तरह से .NET 4.0 के लिए फिर से डिजाइन किया गया है। अब यह बढ़ती आंतरिक बफर को स्टोर करने के लिए स्ट्रिंग बिल्डर्स की एक लिंक वाली सूची रस्सी का उपयोग करता है। यह किसी समस्या के लिए एक समाधान है जब आप प्रारंभिक क्षमता को अच्छी तरह से अनुमान नहीं लगा सकते हैं और टेक्स्ट की मात्रा बड़ी है। इससे बड़े ऑब्जेक्ट हीप को क्लोजिंग करने वाले असंगत आंतरिक बफर की बहुत सारी प्रतियां बनती हैं। संदर्भ स्रोत से उपलब्ध स्रोत कोड से यह टिप्पणी प्रासंगिक है:

// We want to keep chunk arrays out of large object heap (< 85K bytes ~ 40K chars) to be sure. 
    // Making the maximum chunk size big means less allocation code called, but also more waste 
    // in unused characters and slower inserts/replaces (since you do need to slide characters over 
    // within a buffer). 
    internal const int MaxChunkSize = 8000; 
0

StringBuilder.ToString द्वारा प्रदान किए गए इंटरफ़ेस पर दस्तावेज़ की बाधा के बजाए यह केवल एक कार्यान्वयन विस्तार था। तथ्य यह है कि अगर आप कभी दस्तावेज किए गए थे तो आपको अनिश्चित महसूस हो सकता है कि यह मामला है।

पुस्तकें अक्सर कुछ उपयोग करने के तरीके में कुछ अंतर्दृष्टि दिखाने के लिए विस्तार से कार्यान्वयन करती हैं, लेकिन अधिकांश चेतावनी लेते हैं कि कार्यान्वयन परिवर्तन के अधीन है।

एक अच्छा उदाहरण क्यों किसी को कार्यान्वयन विवरण पर भरोसा नहीं करना चाहिए।

मुझे संदेह है कि निर्माता को अपरिवर्तनीय बनाने की सुविधा नहीं थी, लेकिन ToString के कार्यान्वयन का केवल दुष्प्रभाव था।

+1

धन्यवाद, जेफ। मैं समझता हूं कि यह एक कार्यान्वयन विस्तार था और मैं किसी भी तरह से इस पर भरोसा नहीं कर रहा हूं। मैं इस बारे में उत्सुक हूं कि क्यों कार्यान्वयन बदल गया क्योंकि यह अभी भी सही समझ में आता है। –

0

मैं इस से पहले नहीं देखा था, इसलिए यहाँ मेरा अनुमान है: एक StringBuilder की आंतरिक भंडारण अब एक सरल string प्रतीत होता है, लेकिन 'हिस्सा' का एक सेट। ToString इस आंतरिक स्ट्रिंग का संदर्भ वापस नहीं कर सकता है क्योंकि यह अब मौजूद नहीं है।

(Are संस्करण 4.0 StringBuilders अब ropes?)

+1

यह भाग के पेड़ की तुलना में भाग की एक श्रृंखला की तरह दिखता है। – Guffa

5

हाँ, आप सही ढंग से याद है। StringBuilder.ToString विधि आंतरिक बफर को स्ट्रिंग के रूप में वापस करने के लिए उपयोग की जाती है, और इसे उपयोग के रूप में चिह्नित करें ताकि StringBuilder में अतिरिक्त परिवर्तनों को एक नया बफर आवंटित करना पड़े।

चूंकि यह एक कार्यान्वयन विस्तार है, इसका दस्तावेज़ीकरण में उल्लेख नहीं है। यही कारण है कि वे वर्ग के परिभाषित व्यवहार में कुछ तोड़ने के बिना अंतर्निहित कार्यान्वयन को बदल सकते हैं।

जैसा कि आप पोस्ट किए गए कोड से देखते हैं, वहां कोई भी आंतरिक बफर नहीं है, इसके बजाय वर्णों को संग्रह में संग्रहीत किया जाता है, और ToString विधि भाग को एक स्ट्रिंग में एक साथ रखती है।

कार्यान्वयन में इस बदलाव का कारण यह है कि उन्होंने StringBuilder कक्षा वास्तव में उपयोग की जाने वाली जानकारी के बारे में जानकारी एकत्र की है, और निष्कर्ष पर आ गया है कि यह दृष्टिकोण औसत और सबसे खराब स्थिति स्थितियों के बीच बेहतर प्रदर्शन देता है। जहाँ तक मेरा यह कुछ मामलों में यह कॉपी किए बिना स्ट्रिंग वापस आ जाएगी देख सकते हैं

public override string ToString() 
{ 
    string stringValue = this.m_StringValue; 
    int currentThread = this.m_currentThread; 
    if ((currentThread != 0) && (currentThread != InternalGetCurrentThread())) 
    { 
     return string.InternalCopy(stringValue); 
    } 
    if ((2 * stringValue.Length) < stringValue.ArrayLength) 
    { 
     return string.InternalCopy(stringValue); 
    } 
    stringValue.ClearPostNullChar(); 
    this.m_currentThread = 0; 
    return stringValue; 
} 

:

+0

स्ट्रिंगबिल्डर ने अपनी टोस्टिंग() विधि में रस्सियों का उपयोग शुरू करने से पहले एक नई स्ट्रिंग लौटने के लिए स्विच किया, जब माइक्रोसॉफ्ट को एहसास हुआ कि किसी ऑब्जेक्ट को कभी भी गैर-थ्रेड-संरक्षित लेखन पहुंच के लिए बाहरी दुनिया में उजागर किया गया है, जबकि यह उत्परिवर्तनीय था म्यूटेबल होने के लिए माना जाता है (क्योंकि यह जानने का कोई तरीका नहीं है कि कुछ धागा ऑब्जेक्ट लिखने की प्रक्रिया में हो सकता है लेकिन डिस्क पर स्वैप किए जाने के कारण, निलंबित, उच्च प्राथमिकता धागे से पूर्ववत, या जो भी हो) के कारण क्षणिक रूप से देरी हो गई है। – supercat

+1

@supercat: कब तक? आईआईआरसी 2.0 कार्यान्वयन ने आंतरिक बफर वापस कर दिया। 3.0 और 3.5 के रूप में अभी भी 2.0 कोड का उपयोग कर रहे थे, 4.0 अगला संस्करण है। – Guffa

+0

वास्तव में? मुझे लगता है कि 4.0 पहले क्षितिज पर था, पहले बदलाव के बारे में पढ़ना याद है। मैंने सोचा कि परिवर्तन 2.0 के साथ हुआ; दर्शन यह था कि यह 'स्ट्रिंगबिल्डर' के गैर थ्रेड-सुरक्षित उपयोग के लिए पूरी तरह से स्वीकार्य है ताकि इसे मनमाने ढंग से कचरे के पात्रों से भरा स्ट्रिंग वापस कर दिया जा सके; इसके लिए एक स्ट्रिंग वापस करने के लिए ठीक नहीं है जो इसकी जांच के बाद उत्परिवर्तित हो सकता है क्योंकि उस व्यवहार से बहुत सारे कोड तोड़ सकते हैं जो 'स्ट्रिंग' को अपरिवर्तनीय होने की अपेक्षा करता है (बाद में एक स्ट्रिंग पर 'स्ट्रिंग.इनर्न' को कॉल करने के प्रभावों के बारे में सोचें जो बाद में उत्परिवर्तित हो !)। – supercat

2

यहाँ परावर्तक से StringBuilder.ToString की .NET 1.1 कार्यान्वयन है। हालांकि, मुझे नहीं लगता कि StringBuilder अपरिवर्तनीय हो जाता है। इसके बजाय मुझे लगता है कि अगर आप StringBuilder पर लिखना जारी रखते हैं तो यह कॉपी-ऑन-राइट का उपयोग करेगा।

+0

धन्यवाद! .NET 1.1 कार्यान्वयन जोड़ने के लिए +1 –

संबंधित मुद्दे