سؤال

في .NET، سلاسل غير قابلة للتغيير وهي متغيرات نوع المرجع. غالبا ما يأتي هذا بمثابة مفاجأة لمطوري .NET الأحدث الذين قد يخطئونهم كائنات نوع القيمة بسبب سلوكهم. ومع ذلك، بخلاف ممارسة استخدام StringBuilder للحصول على سلسلة طويلة esp. في الحلقات، هل هناك أي سبب في الممارسة العملية التي يحتاج المرء لمعرفة هذا التمييز؟

ما سيناريوهات العالم الحقيقي ساعد أو تجنبه من خلال فهم التمييز المرجعي للقيمة فيما يتعلق بأسلوت .NET مقابل التظاهر / سوء فهمها لتكون أنواع القيمة؟

هل كانت مفيدة؟

المحلول

تصميم stringكان S كان عمدا بحيث لا تحتاج إلى القلق أكثر من ذلك بكثير كمبرمج. في العديد من المواقف، هذا يعني أنه يمكنك فقط تعيين، نقل، نسخ، تغيير السلاسل دون التفكير في الكثير من العواقب المعقدة المحتملة إذا كانت هناك إشارة أخرى إلى سلسلة، وسيتم تغييرها في نفس الوقت (كما يحدث مع مراجع كائن).

معلمات السلسلة في استدعاء طريقة

(تحرير: هذا القسم المضافة لاحقا)
عند تمرير السلاسل إلى طريقة، يتم تمريرها بالرجوع إليها. عندما يتم قراءتها فقط في الجسم الأسلوب، لا يحدث شيء خاص. ولكن عند تغييرها، يتم إنشاء نسخة واستخدام المتغير المؤقت في بقية الطريقة. وتسمى هذه العملية نسخ على الكتابة.

ما المشاكل الصغار هو أنه يتم استخدامها لحقيقة أن الكائنات هي المراجع وتغييرها بطريقة تتغير المعلمة المرة. أن تفعل الشيء نفسه مع سلاسل، يحتاجون إلى استخدام ref الكلمة الرئيسية. يسمح هذا بالفعل بتغيير مرجع السلسلة وإرجاعها إلى وظيفة الاتصال. إذا لم تكن كذلك، لا يمكن تغيير السلسلة بواسطة هيئة الطريقة:

void ChangeBad(string s)      { s = "hello world"; }
void ChangeGood(ref string s) { s = "hello world"; }

// in calling method:
string s1 = "hi";
ChangeBad(s1);       // s1 remains "hi" on return, this is often confusing
ChangeGood(ref s1);  // s1 changes to "hello world" on return

على stringbuilder.

هذا التمييز مهم، لكن المبرمجين المبتدئين عادة ما يكونون أفضل حالا لا يعرفون الكثير عن ذلك. استخدام StringBuilder عند القيام بالكثير من سلسلة "المبنى" جيدة، ولكن في كثير من الأحيان، سيكون تطبيقك المزيد من الأسماك إلى تقلى واكتساب الأداء الصغير StringBuilder لا يكاد يذكر. كن حذرا من المبرمجين الذين يخبرونك بذلك الكل يجب إجراء معالجة السلسلة باستخدام StringBuilder.

كقاعدة خشنة للغاية للإبهام: Stringbuilder لديه بعض تكلفة الإبداع، ولكن إلحاق رخيص. تحتوي السلسلة على تكلفة إنشاء رخيصة، ولكن تسلسل مكلفة نسبيا. تبلغ نقطة الدوران حوالي 400-500 سلسلة، اعتمادا على الحجم: بعد ذلك، يصبح Stringbuilder أكثر كفاءة.

المزيد حول StringBuilder VS أداء سلسلة

تحرير: بناء على تعليق من كونراد رودولف، أضفت هذا القسم.

إذا كانت قاعدة الإبهام السابقة تجعلك تتساءل، ففكر في تفسيرات أكثر تفصيلا قليلا:

  • Stransbuilder مع العديد من سلسلة صغيرة يلفظ تسلسل السلسلة بسرعة إلى حد ما (30، 50 إلحاقا)، ولكن على 2μs، فإن زيادة الأداء بنسبة 100٪ غالبا ما تكون ضئيلة (آمنة لبعض المواقف النادرة)؛
  • Strandbuilder مع بعض الإلحاق سلسلة كبيرة (80 حرفا أو سلاسل أكبر) يفوق سلسلة سلسلة فقط بعد الآلاف، وأحيانا مئات الآلاف من التكرارات والفرق في كثير من الأحيان مجرد عدد قليل من النسبات النسبية؛
  • غالبا ما يجعل عمليات خلط الإجراءات (استبدال وإدراج أو Substring و Regex وغيرها) باستخدام سلسلة StringBuilder أو سلسلة متساوية؛
  • يمكن تحسين تسلسل سلسلة الثوابت بعيدا عن طريق المترجم أو CLR أو JIT، لا يمكنه ل Stringbuilder؛
  • الكود غالبا ما يمزج تسلسل +, StringBuilder.Append, String.Format, ToString وغيرها من عمليات السلسلة، باستخدام Stransbuilder في مثل هذه الحالات هي بالكاد فعالة من أي وقت مضى.

اذن متى يكون انها فعالة؟ في الحالات التي يتم فيها إلحاق العديد من السلاسل الصغيرة، أي تسلسل البيانات إلى ملف، على سبيل المثال، وعندما لا تحتاج إلى تغيير البيانات "المكتوبة" بمجرد "الكتابة" إلى StringBuilder. وفي الحالات التي تحتاج فيها العديد من الطرق إلى إلحاق شيء ما، نظرا لأن STRANSBUILDER هو نوع مرجعي ويتم نسخ السلاسل عند تغييرها.

على الأوتار المعتشرة

ترتفع المشكلة - ليس فقط مع مبرمجات صغار - عندما يحاولون القيام بمقارنة مرجعية ومعرفة ذلك في بعض الأحيان تكون النتيجة صحيحة، وأحيانا تكون خاطئة، في نفس المواقف على ما يبدو. ماذا حدث؟ عند انتقال السلاسل من قبل المحول البرمجي وإضافتها إلى مجموعة سلاسل ثابتة ثابتة من السلاسل، يمكن أن تشير المقارنة بين سلسلتين إلى نفس عنوان الذاكرة. متى (مرجع!) مقارنة بين سلاسل متساوية، واحدة معتادة ولا، لن تسفر عن خطأ. يستخدم = المقارنة، أو Equals ولا تلعب مع ReferenceEquals عند التعامل مع السلاسل.

على string.empty.

في نفس الدوري يناسب سلوكا غريبا يحدث في بعض الأحيان عند استخدام String.Empty: ثابت String.Empty متدرب دائما، ولكن متغير مع قيمة مخصصة ليست كذلك. ومع ذلك، بشكل افتراضي سوف يعين المحول البرمجي String.Empty وأشر إلى نفس عنوان الذاكرة. النتيجة: متغير سلسلة متغيرة، بالمقارنة مع ReferenceEquals, ، يعود صحيحا، بينما قد تتوقع خطأ بدلا من ذلك.

// emptiness is treated differently:
string empty1 = String.Empty;
string empty2 = "";
string nonEmpty1 = "something";
string nonEmpty2 = "something";

// yields false (debug) true (release)
bool compareNonEmpty = object.ReferenceEquals(nonEmpty1, nonEmpty2);

// yields true (debug) false (release, depends on .NET version and how it's assigned)
bool compareEmpty = object.ReferenceEquals(empty1, empty2);

في الصميم

سألت أساسا عن ما يمكن أن تحدث المواقف إلى غير المبللة. أعتقد أن وجهة نظري تغلي لتجنب object.ReferenceEquals لأنه لا يمكن الوثوق بها عند استخدامها مع السلاسل. والسبب هو أن سلسلة Interning تستخدم عندما تكون السلسلة ثابتة في التعليمات البرمجية، ولكن ليس دائما. لا يمكنك الاعتماد على هذا السلوك. على أية حال String.Empty و "" يتم إدراجها دائما، ليس عندما يعتقد المترجم أن القيمة قابلة للتغيير. خيارات التحسين المختلفة (Debug VS الإصدار وغيرها) سيؤدي إلى نتائج مختلفة.

متي فعل انت تحتاج ReferenceEquals على أي حال؟ مع أشياء فمن المنطقي، ولكن مع سلاسل لا. تعليم أي شخص يعمل مع سلاسل لتجنب استخدامه ما لم يفهموا أيضا unsafe وكائنات مثبتة.

أداء

عندما يكون الأداء مهما، يمكنك معرفة هذه السلاسل هي في الواقع ليس ثابت وأن استخدام StringBuilder يكون ليس دائما أسرع نهج.

الكثير من المعلومات التي استخدمتها هنا مفصلة في هذه المادة الممتازة عن السلاسل, ، جنبا إلى جنب مع "كيفية" لمعالجة السلسلة في مكان (سلاسل قابلة للتغيير).

تحديث: نموذج التعليمات البرمجية المضافة
تحديث: وأضاف قسم "عمق" (آمل أن يجد شخص ما هذا مفيدا؛)
تحديث: إضافة بعض الروابط، قسم المضافة في سلسلة سلسلة
تحديث: تقدير إضافي عند التبديل من السلاسل إلى StringBuilder
تحديث: وأضاف قسم إضافي على أداء سلسلة StringBuilder مقابل سلسلة، بعد ملاحظة بواسطة Konrad Rudolph

نصائح أخرى

التمييز الوحيد الذي يهم حقا لمعظم الكود هو حقيقة ذلك null يمكن تعيينها إلى سلسلة متغيرات.

تعمل فئة ثابتة مثل نوع القيمة في جميع المواقف الشائعة، ويمكنك القيام بالكثير من البرمجة دون رعاية الكثير عن الفرق.

إنه عندما تقوم بحفر أعمق وأعتقد قليلا في الأداء الذي لديك استخدام حقيقي للتمييز. على سبيل المثال لمعرفة أنه على الرغم من تمرير سلسلة كمعلمة إلى أسلوب يعمل كما لو تم إنشاء نسخة من السلسلة، فإن النسخ لا يحدث بالفعل. قد يكون هذا مفاجأة للأشخاص الذين يستخدمون اللغات حيث توجد سلاسل بالفعل أنواع قيمة (مثل VB6؟)، ويمر الكثير من السلاسل كمعلمات لن تكون جيدة للأداء.

السلسلة هي سلالة خاصة. وهي نوع مرجعي يستخدمها معظم المبرمجين كوعي قيمة. من خلال جعلها ثابتة واستخدام مسبح المتدرب، فإنه يعمل على تحسين استخدام الذاكرة الذي سيكون ضخما إذا كان نوع قيمة نقية.

المزيد من القراءات هنا:
كائن سلسلة C # .NET هو حقا بالرجوع؟ على ذلك
طريقة string.intern على MSDN
سلسلة (مرجع C #) على MSDN

تحديث:
يرجى الرجوع إلى abelتعليق على هذا المنصب. قام بتصحيح بياني المضلل.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top