سؤال

لنفترض أنك تريد إخراج سلاسل أو ربطها.أي من الأساليب التالية تفضل؟

  • var p = new { FirstName = "Bill", LastName = "Gates" };

  • Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

  • Console.WriteLine(p.FirstName + " " + p.LastName);

هل تفضل استخدام التنسيق أم أنك تقوم ببساطة بربط السلاسل؟ما هو المفضل لديك؟هل واحدة من هذه تؤذي عينيك؟

هل لديك أي حجج عقلانية لاستخدام أحدهما دون الآخر؟

سأذهب للثانية.

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

المحلول

جرب هذا الرمز.

إنها نسخة معدلة قليلاً من التعليمات البرمجية الخاصة بك.
1.لقد قمت بإزالة Console.WriteLine لأنه من المحتمل أن يكون عدد قليل من الطلبات أبطأ مما أحاول قياسه.
2.أقوم بتشغيل ساعة الإيقاف قبل الحلقة وأوقفها مباشرة بعد ذلك، وبهذه الطريقة لا أفقد الدقة إذا كانت الوظيفة تستغرق على سبيل المثال 26.4 علامة للتنفيذ.
3.الطريقة التي قسمت بها النتيجة على بعض التكرارات كانت خاطئة.انظر ماذا يحدث إذا كان لديك 1000 مللي ثانية و100 مللي ثانية.في كلتا الحالتين، سوف تحصل على 0 مللي ثانية بعد تقسيمها على 1000000.

Stopwatch s = new Stopwatch();

var p = new { FirstName = "Bill", LastName = "Gates" };

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (p.FirstName + " " + p.LastName);
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();
s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", p.FirstName, p.LastName);
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();


Console.Clear();
Console.WriteLine(n.ToString()+" x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Thread.Sleep(4000);

تلك هي نتائجي:

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName);أخذ:618 مللي ثانية - 2213706 علامة
1000000 x النتيجة = (p.FirstName + " " + p.LastName);أخذ:166 مللي ثانية - 595610 علامة

نصائح أخرى

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

التحسين المبكر = فشل.

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

ضع في اعتبارك ما إذا كان عميلك يريد تغييره لاحقًا حتى يتمكن من تكوين ما إذا كان سيتم عرضه أم لا "Firstname Lastname" أو "Lastname, Firstname." مع خيار التنسيق، يكون هذا أمرًا سهلاً - فقط قم بتبديل سلسلة التنسيق.مع concat، ستحتاج إلى رمز إضافي.من المؤكد أن هذا لا يبدو أمرًا مهمًا في هذا المثال بالتحديد ولكن استقراء.

يا عزيزي - بعد قراءة أحد الردود الأخرى حاولت عكس ترتيب العمليات - فقم بإجراء التسلسل أولاً، ثم String.Format...

Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 8ms - 30488 ticks
Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 0ms - 182 ticks

لذا فإن ترتيب العمليات يحدث فرقًا كبيرًا، أو بالأحرى تكون العملية الأولى دائمًا أبطأ بكثير.

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

Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 5ms - 20335 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 156 ticks
Bill Gates
Console.WriteLine(FirstName + " " + LastName); took: 0ms - 122 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 181 ticks
Bill Gates
Console.WriteLine("{0} {1}", FirstName, LastName); took: 0ms - 122 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 142 ticks
Bill Gates
String.Concat(FirstName, " ", LastName); took: 0ms - 117 ticks

كما ترون، فإن عمليات التشغيل اللاحقة لنفس الطريقة (لقد قمت بإعادة هيكلة الكود إلى 3 طرق) تكون أسرع بشكل متزايد.يبدو أن أسرع طريقة هي طريقة Console.WriteLine(String.Concat(...)) متبوعة بالتسلسل العادي، ثم العمليات المنسقة.

من المحتمل أن يكون التأخير الأولي في بدء التشغيل هو تهيئة تدفق وحدة التحكم، حيث أن وضع Console.Writeline("Start!") قبل العملية الأولى يعيد جميع الأوقات إلى طبيعتها.

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

جرب هذا للحجم:

Stopwatch s = new Stopwatch();

int n = 1000000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0, sbElapsedMilliseconds = 0, sbElapsedTicks = 0;

Random random = new Random(DateTime.Now.Millisecond);

string result;
s.Start();
for (var i = 0; i < n; i++)
    result = (random.Next().ToString() + " " + random.Next().ToString());
s.Stop();
cElapsedMilliseconds = s.ElapsedMilliseconds;
cElapsedTicks = s.ElapsedTicks;
s.Reset();

s.Start();
for (var i = 0; i < n; i++)
    result = string.Format("{0} {1}", random.Next().ToString(), random.Next().ToString());
s.Stop();
fElapsedMilliseconds = s.ElapsedMilliseconds;
fElapsedTicks = s.ElapsedTicks;
s.Reset();

StringBuilder sb = new StringBuilder();
s.Start();
for(var i = 0; i < n; i++){
    sb.Clear();
    sb.Append(random.Next().ToString());
    sb.Append(" ");
    sb.Append(random.Next().ToString());
    result = sb.ToString();
}
s.Stop();
sbElapsedMilliseconds = s.ElapsedMilliseconds;
sbElapsedTicks = s.ElapsedTicks;
s.Reset();

Console.WriteLine(n.ToString() + " x result = string.Format(\"{0} {1}\", p.FirstName, p.LastName); took: " + (fElapsedMilliseconds) + "ms - " + (fElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x result = (p.FirstName + \" \" + p.LastName); took: " + (cElapsedMilliseconds) + "ms - " + (cElapsedTicks) + " ticks");
Console.WriteLine(n.ToString() + " x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(\" \"); sb.Append(random.Next().ToString()); result = sb.ToString(); took: " + (sbElapsedMilliseconds) + "ms - " + (sbElapsedTicks) + " ticks");
Console.WriteLine("****************");
Console.WriteLine("Press Enter to Quit");
Console.ReadLine();

إخراج العينة:

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 513ms - 1499816 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 393ms - 1150148 ticks
1000000 x sb.Clear();sb.Append(random.Next().ToString()); sb.Append(" "); sb.Append(random.Next().ToString()); result = sb.ToString(); took: 405ms - 1185816 ticks

شفقة على المترجمين الفقراء

اذا أنت يعرف سيبقى طلبك باللغة الإنجليزية، حسنًا، احفظ دقات الساعة.ومع ذلك، فإن العديد من الثقافات عادةً ما ترى اسم العائلة والاسم الأول في العناوين، على سبيل المثال.

لذا استخدم string.Format(), ، خاصة إذا كنت تريد أن يتم إرسال طلبك إلى أي مكان حيث اللغة الإنجليزية ليست هي اللغة الأولى.

فيما يلي نتائجي لأكثر من 100000 تكرار:

Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took (avg): 0ms - 689 ticks
Console.WriteLine(p.FirstName + " " + p.LastName); took (avg): 0ms - 683 ticks

وهنا هو رمز مقاعد البدلاء:

Stopwatch s = new Stopwatch();

var p = new { FirstName = "Bill", LastName = "Gates" };

//First print to remove the initial cost
Console.WriteLine(p.FirstName + " " + p.LastName);
Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

int n = 100000;
long fElapsedMilliseconds = 0, fElapsedTicks = 0, cElapsedMilliseconds = 0, cElapsedTicks = 0;

for (var i = 0; i < n; i++)
{
    s.Start();
    Console.WriteLine(p.FirstName + " " + p.LastName);
    s.Stop();
    cElapsedMilliseconds += s.ElapsedMilliseconds;
    cElapsedTicks += s.ElapsedTicks;
    s.Reset();
    s.Start();
    Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
    s.Stop();
    fElapsedMilliseconds += s.ElapsedMilliseconds;
    fElapsedTicks += s.ElapsedTicks;
    s.Reset();
}

Console.Clear();

Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took (avg): " + (fElapsedMilliseconds / n) + "ms - " + (fElapsedTicks / n) + " ticks");
Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took (avg): " + (cElapsedMilliseconds / n) + "ms - " + (cElapsedTicks / n) + " ticks");

لذلك ، لا أعرف من الذي يجب وضع علامة على الرد كإجابة :)

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

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

إذا كنت تستخدم ‎.NET 3.5، فيمكنك استخدام إحدى طرق الامتداد مثل هذه واحصل على بناء جملة سلس وسهل مثل هذا:

string str = "{0} {1} is my friend. {3}, {2} is my boss.".FormatWith(prop1,prop2,prop3,prop4);

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

string name = String.Format(ApplicationStrings.General.InformalUserNameFormat,this.FirstName,this.LastName);

بالنسبة للمعالجة البسيطة جدًا، سأستخدم التسلسل، ولكن بمجرد تجاوز عنصرين أو ثلاثة عناصر، يصبح التنسيق IMO أكثر ملاءمة.

سبب آخر لتفضيل String.Format هو أن سلاسل .NET غير قابلة للتغيير والقيام بذلك بهذه الطريقة يؤدي إلى إنشاء عدد أقل من النسخ المؤقتة/الوسيطة.

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

باستخدام الكود التالي:

    System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();

    var p = new { FirstName = "Bill", LastName = "Gates" };

    s.Start();
    Console.WriteLine("{0} {1}", p.FirstName, p.LastName);
    s.Stop();
    Console.WriteLine("Console.WriteLine(\"{0} {1}\", p.FirstName, p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");

    s.Reset();
    s.Start();
    Console.WriteLine(p.FirstName + " " + p.LastName);
    s.Stop();

    Console.WriteLine("Console.WriteLine(p.FirstName + \" \" + p.LastName); took: " + s.ElapsedMilliseconds + "ms - " + s.ElapsedTicks + " ticks");

حصلت على النتائج التالية:

Bill Gates
Console.WriteLine("{0} {1}", p.FirstName, p.LastName); took: 2ms - 7280 ticks
Bill Gates
Console.WriteLine(p.FirstName + " " + p.LastName); took: 0ms - 67 ticks

استخدام طريقة التنسيق أبطأ بأكثر من 100 مرة !!لم يتم تسجيل التسلسل حتى على أنه 1 مللي ثانية، ولهذا السبب قمت بإخراج علامات المؤقت أيضًا.

بالنسبة لتسلسل السلاسل الأساسية، عادةً ما أستخدم النمط الثاني - وهو أسهل في القراءة وأبسط.ومع ذلك، إذا كنت أقوم بتركيبة سلسلة أكثر تعقيدًا، عادةً ما أختار String.Format.

String.Format يحفظ الكثير من علامات الاقتباس والإيجابيات ...

Console.WriteLine("User {0} accessed {1} on {2}.", user.Name, fileName, timestamp);
vs
Console.WriteLine("User " + user.Name + " accessed " + fileName + " on " + timestamp + ".");

تم حفظ عدد قليل فقط من الأحرف، ولكن أعتقد، في هذا المثال، أن التنسيق يجعل الأمر أكثر وضوحًا.

سيكون الاختبار الأفضل هو مراقبة ذاكرتك باستخدام عدادات الذاكرة Perfmon وCLR.ما أفهمه هو أن السبب الكامل وراء رغبتك في استخدام String.Format بدلاً من مجرد تسلسل السلاسل هو أنه نظرًا لأن السلاسل غير قابلة للتغيير، فإنك تثقل كاهل أداة تجميع البيانات المهملة بلا داعٍ بسلاسل مؤقتة تحتاج إلى استعادتها في المسار التالي.

StringBuilder وString.Format، على الرغم من احتمالية بطئهما، إلا أنهما أكثر كفاءة في استخدام الذاكرة.

ما هو السيء جدًا في تسلسل السلسلة؟

بشكل عام أفضّل الخيار الأول، خصوصًا عندما تصبح الأوتار طويلة، قد يكون من الأسهل قراءتها.

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

ومع ذلك، تجدر الإشارة إلى أنه إذا كانت المعلمات التي تمررها إلى String.Format (والطرق الأخرى مثل Console.Write) هي أنواع قيم، فسيتم وضعها في صندوق قبل تمريرها، مما يمكن أن يوفر نتائج الأداء الخاصة بها. بلوق وظيفة على هذا هنا.

بعد أسبوع من الآن، 19 أغسطس 2015، سيكون عمر هذا السؤال سبع (7) سنوات بالضبط.هناك الآن طريقة أفضل للقيام بذلك. أحسن من حيث قابلية الصيانة لأنني لم أقم بأي اختبار للأداء مقارنةً بسلاسل متسلسلة فقط (ولكن هل هذا مهم هذه الأيام؟بضعة ميلي ثانية في الفرق؟).الطريقة الجديدة للقيام بذلك مع ج#6.0:

var p = new { FirstName = "Bill", LastName = "Gates" };
var fullname = $"{p.FirstName} {p.LastName}";

هذه الميزة الجديدة هي أحسن, ، المنظمة البحرية الدولية، و في الواقع أفضل في حالتنا نظرًا لأن لدينا رموزًا نبني فيها سلاسل استعلام تعتمد قيمها على بعض العوامل.تخيل سلسلة استعلام واحدة حيث لدينا 6 وسيطات.فبدلاً من القيام، على سبيل المثال:

var qs = string.Format("q1={0}&q2={1}&q3={2}&q4={3}&q5={4}&q6={5}", 
    someVar, anotherVarWithLongName, var3, var4, var5, var6)

in يمكن كتابتها بهذه الطريقة ومن الأسهل قراءتها:

var qs=$"q1={someVar}&q2={anotherVarWithLongName}&q3={var3}&q4={var4}&q5={var5}&q6={var6}";

بدءًا من الإصدار C#6.0 سلاسل محرف يمكن استخدامها للقيام بذلك، مما يبسط التنسيق أكثر.

var name = "Bill";
var surname = "Gates";
MessageBox.Show($"Welcome to the show, {name} {surname}!");

يبدو تعبير السلسلة المحرف كسلسلة قالب تحتوي على تعبيرات.يقوم تعبير السلسلة المحرف بإنشاء سلسلة عن طريق استبدال التعبيرات المضمنة بتمثيلات ToString لنتائج التعبيرات.

تتمتع السلاسل المحرفة بأداء مماثل لـ String.Format، ولكنها تتمتع بقابلية قراءة محسنة وصياغة أقصر، وذلك نظرًا لإدراج القيم والتعبيرات في السطر.

يرجى الرجوع أيضا إلى هذه المقالة dotnetperls على الاستيفاء سلسلة.

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

  1. التنسيق هو طريقة ".NET" للقيام بذلك.بعض أدوات إعادة البناء (Refactor!لأحد) سيقترح أيضًا إعادة هيكلة التعليمات البرمجية ذات النمط المتزامن لاستخدام نمط التنسيق.
  2. يعد التنسيق أسهل في التحسين للمترجم (على الرغم من أنه من المحتمل إعادة هيكلة الثاني لاستخدام طريقة "Concat" السريعة).
  3. عادةً ما يكون التنسيق أكثر وضوحًا للقراءة (خاصة مع التنسيق "الفاخر").
  4. التنسيق يعني استدعاءات ضمنية لـ '.ToString' على كافة المتغيرات، وهو أمر جيد لسهولة القراءة.
  5. وفقًا لـ "Effective C#"، تم إفساد تطبيقات .NET 'WriteLine' و'Format، حيث يتم وضع جميع أنواع القيم تلقائيًا (وهو أمر سيء).ينصح "Effective C#" بإجراء مكالمات ".ToString" بشكل صريح، والتي تعتبر IMHO زائفة (راجع نشر جيف)
  6. في الوقت الحالي، لا يقوم المترجم بالتحقق من تلميحات نوع التنسيق، مما يؤدي إلى حدوث أخطاء في وقت التشغيل.ومع ذلك، يمكن تعديل هذا في الإصدارات المستقبلية.

سأستخدم String.Format، لكن سيكون لدي أيضًا سلسلة التنسيق في ملفات الموارد بحيث يمكن ترجمتها للغات أخرى.لا يسمح لك استخدام سلسلة بسيطة بالقيام بذلك.من الواضح أنك إذا لم تكن بحاجة إلى ترجمة هذه السلسلة، فهذا ليس سببًا للتفكير فيه.يعتمد الأمر حقًا على الغرض من السلسلة.

إذا كان سيتم عرضه للمستخدم، فسأستخدم String.Format حتى أتمكن من الترجمة إذا كنت بحاجة إلى ذلك - و FxCop سوف أقوم بالتدقيق الإملائي لي، فقط في حالة :)

إذا كان يحتوي على أرقام أو أي أشياء أخرى غير سلسلة (على سبيل المثال.التواريخ)، سأستخدم String.Format لأنه يمنحني المزيد السيطرة على التنسيق.

إذا كان الأمر يتعلق بإنشاء استعلام مثل SQL، فسأستخدمه لينك.

إذا كنت تريد توصيل السلاسل داخل حلقة، فسأستخدمها StringBuilder لتجنب مشاكل الأداء.

إذا كان الأمر يتعلق ببعض المخرجات التي لن يراها المستخدم، ولن يؤثر على الأداء، فسأستخدم String.Format لأنني معتاد على استخدامه على أي حال وأنا معتاد عليه :)

إذا كنت تتعامل مع شيء يجب أن يكون سهل القراءة (وهذا هو معظم التعليمات البرمجية)، فسألتزم بإصدار التحميل الزائد للمشغل ما لم:

  • يجب تنفيذ الكود ملايين المرات
  • أنت تقوم بالكثير من concats (أكثر من 4 طن)
  • يستهدف الكود إطار العمل المضغوط

في ظل اثنتين على الأقل من هذه الظروف، سأستخدم StringBuilder بدلاً من ذلك.

اخترت على أساس سهولة القراءة.أفضّل خيار التنسيق عندما يكون هناك بعض النص حول المتغيرات.في هذا المثال:

Console.WriteLine("User {0} accessed {1} on {2}.", 
                   user.Name, fileName, timestamp);

أنت تفهم المعنى حتى بدون الأسماء المتغيرة، بينما الجملة مكتظة بعلامات الاقتباس وعلامات + وتربك عيني:

Console.WriteLine("User " + user.Name + " accessed " + fileName + 
                  " on " + timestamp + ".");

(لقد استعرت مثال مايك لأنني أحبه)

إذا كانت سلسلة التنسيق لا تعني الكثير بدون أسماء المتغيرات، فلا بد لي من استخدام concat:

   Console.WriteLine("{0} {1}", p.FirstName, p.LastName);

خيار التنسيق يجعلني أقرأ أسماء المتغيرات وأقوم بتعيينها على الأرقام المقابلة.خيار concat لا يتطلب ذلك.ما زلت في حيرة من أمري بين علامات الاقتباس و+، ولكن البديل أسوأ.روبي؟

   Console.WriteLine(p.FirstName + " " + p.LastName);

من ناحية الأداء، أتوقع أن يكون خيار التنسيق أبطأ من خيار concat، نظرًا لأن التنسيق يتطلب أن تكون السلسلة تحليل.لا أتذكر أنني اضطررت إلى تحسين هذا النوع من التعليمات، ولكن إذا فعلت ذلك، فسوف أنظر string أساليب مثل Concat() و Join().

الميزة الأخرى للتنسيق هي أنه يمكن وضع سلسلة التنسيق في ملف التكوين.مفيد جدًا في التعامل مع رسائل الخطأ ونص واجهة المستخدم.

إذا كنت تنوي توطين النتيجة، فإن String.Format ضروري لأن اللغات الطبيعية المختلفة قد لا تحتوي حتى على البيانات بنفس الترتيب.

أعتقد أن هذا يعتمد بشكل كبير على مدى تعقيد الإخراج.أميل إلى اختيار السيناريو الأفضل في ذلك الوقت.

اختر الأداة المناسبة بناءً على المهمة:D أيهما يبدو أنظف!

أنا أفضل الخيار الثاني أيضًا، ولكن ليس لدي أي حجج عقلانية في الوقت الحالي لدعم هذا الموقف.

هذا لطيف!

فقط اضافة

        s.Start();
        for (var i = 0; i < n; i++)
            result = string.Concat(p.FirstName, " ", p.LastName);
        s.Stop();
        ceElapsedMilliseconds = s.ElapsedMilliseconds;
        ceElapsedTicks = s.ElapsedTicks;
        s.Reset();

وهو أسرع (أعتقد أنه تم استدعاء string.Concat في كلا المثالين، لكن المثال الأول يتطلب نوعًا من الترجمة).

1000000 x result = string.Format("{0} {1}", p.FirstName, p.LastName); took: 249ms - 3571621 ticks
1000000 x result = (p.FirstName + " " + p.LastName); took: 65ms - 944948 ticks
1000000 x result = string.Concat(p.FirstName, " ", p.LastName); took: 54ms - 780524 ticks

وبما أنني لا أعتقد أن الإجابات هنا تغطي كل شيء، أود أن أقوم بإضافة صغيرة هنا.

Console.WriteLine(string format, params object[] pars) المكالمات string.Format.يشير "+" إلى تسلسل السلسلة.لا أعتقد أن هذا له علاقة دائمًا بالأسلوب؛أميل إلى المزج بين الأسلوبين اعتمادًا على السياق الذي أتواجد فيه.

اجابة قصيرة

القرار الذي تواجهه يتعلق بتخصيص السلسلة.سأحاول أن أجعل الأمر بسيطًا.

قل أن لديك

string s = a + "foo" + b;

إذا قمت بتنفيذ ذلك، سيتم تقييمه على النحو التالي:

string tmp1 = a;
string tmp2 = "foo" 
string tmp3 = concat(tmp1, tmp2);
string tmp4 = b;
string s = concat(tmp3, tmp4);

tmp هنا ليس متغيرًا محليًا حقًا، ولكنه مؤقت لـ JIT (يتم دفعه على مكدس IL).إذا قمت بدفع سلسلة على المكدس (مثل ldstr في IL للقيم الحرفية)، يمكنك وضع مرجع لمؤشر سلسلة على المكدس.

لحظة اتصالك concat يصبح هذا المرجع مشكلة، لأنه لا يوجد أي مرجع سلسلة متاح يحتوي على كلتا السلسلتين.وهذا يعني أن .NET يحتاج إلى تخصيص كتلة جديدة من الذاكرة، ثم ملؤها بالسلسلتين.سبب هذه المشكلة هو أن التخصيص مكلف نسبيًا.

مما يغير السؤال إلى:كيف يمكنك تقليل عدد concat عمليات؟

إذن الجواب التقريبي هو: string.Format بالنسبة لـ >1 concats، سيعمل "+" بشكل جيد لـ 1 concats.وإذا كنت لا تهتم بإجراء تحسينات صغيرة للأداء، string.Format سوف تعمل بشكل جيد في الحالة العامة.

ملاحظة حول الثقافة

ثم هناك ما يسمى بالثقافة..

string.Format تمكنك من استخدام CultureInfo في التنسيق الخاص بك.يستخدم عامل التشغيل البسيط "+" الثقافة الحالية.

هذه ملاحظة مهمة بشكل خاص إذا كنت تكتب تنسيقات ملفات وf.ex. double القيم التي "تضيفها" إلى السلسلة.على الأجهزة المختلفة، قد ينتهي بك الأمر بسلاسل مختلفة إذا لم تستخدمها string.Format مع صريحة CultureInfo.

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

إجابة أكثر تفصيلا

إذا كنت لا تعرف الحجم الدقيق للسلسلة مسبقًا، فمن الأفضل استخدام سياسة كهذه لتخصيص المخازن المؤقتة التي تستخدمها بشكل عام.يتم ملء المساحة الفارغة أولاً، وبعد ذلك يتم نسخ البيانات.

النمو يعني تخصيص كتلة جديدة من الذاكرة ونسخ البيانات القديمة إلى المخزن المؤقت الجديد.يمكن بعد ذلك تحرير الكتلة القديمة من الذاكرة.يمكنك الحصول على النتيجة النهائية في هذه المرحلة:النمو عملية مكلفة.

الطريقة الأكثر عملية للقيام بذلك هي استخدام سياسة التخصيص الزائد.السياسة الأكثر شيوعًا هي توزيع المخازن المؤقتة في صلاحيات 2.بالطبع، عليك أن تفعل ذلك بشكل أكثر ذكاءً من ذلك (نظرًا لأنه ليس من المنطقي أن تنمو من 1،2،4،8 إذا كنت تعلم بالفعل أنك بحاجة إلى 128 حرفًا) ولكنك حصلت على الصورة.تضمن السياسة أنك لا تحتاج إلى الكثير من العمليات الباهظة الثمن التي وصفتها أعلاه.

StringBuilder هي فئة تقوم بشكل أساسي بتخصيص المخزن المؤقت الأساسي بصلاحيات اثنين. string.Format الاستخدامات StringBuilder تحت الغطاء.

وهذا يجعل قرارك عبارة عن مقايضة أساسية بين التخصيص والإلحاق (-متعدد) (w/w.o).الثقافة) أو مجرد تخصيص وإلحاق.

شخصيًا، الخيار الثاني هو أن كل ما تستخدمه يكون بالترتيب المباشر الذي سيتم إخراجه به.بينما مع الأول، يجب عليك مطابقة {0} و{1} مع المتغير المناسب، وهو أمر يسهل العبث به.

على الأقل ليس سيئًا مثل C++ sprintf حيث إذا أخطأت في نوع المتغير فسوف ينفجر الأمر برمته.

أيضًا، نظرًا لأن الثاني كله مضمن ولا يحتاج إلى إجراء أي بحث واستبدال لجميع الأشياء {0}، فيجب أن يكون الأخير أسرع...على الرغم من أنني لا أعرف على وجه اليقين.

أنا في الواقع أحب الخيار الأول لأنه عندما يكون هناك الكثير من المتغيرات المتداخلة مع النص، يبدو لي أنه من الأسهل قراءته.بالإضافة إلى أنه من الأسهل التعامل مع علامات الاقتباس عند استخدام string.Format() أه التنسيق.هنا تحليل لائق من تسلسل السلسلة.

لقد اتبعت دائمًا طريق string.Format().تعد القدرة على تخزين التنسيقات في متغيرات مثل مثال ناثان ميزة عظيمة.في بعض الحالات، قد أقوم بإلحاق متغير ولكن بمجرد ربط أكثر من متغير واحد، أقوم بإعادة البناء لاستخدام التنسيق.

أوه، وفقط للاكتمال، ما يلي هو بضع علامات التجزئة أسرع من التسلسل العادي:

Console.WriteLine(String.Concat(p.FirstName," ",p.LastName));

الأول (التنسيق) يبدو أفضل بالنسبة لي.إنها أكثر قابلية للقراءة ولا تقوم بإنشاء كائنات سلسلة مؤقتة إضافية.

كنت أشعر بالفضول بشأن موقف StringBuilder من هذه الاختبارات.النتائج أدناه...

class Program {
   static void Main(string[] args) {

      var p = new { FirstName = "Bill", LastName = "Gates" };

      var tests = new[] {
         new { Name = "Concat", Action = new Action(delegate() { string x = p.FirstName + " " + p.LastName; }) },
         new { Name = "Format", Action = new Action(delegate() { string x = string.Format("{0} {1}", p.FirstName, p.LastName); }) },
         new { Name = "StringBuilder", Action = new Action(delegate() {
            StringBuilder sb = new StringBuilder();
            sb.Append(p.FirstName);
            sb.Append(" ");
            sb.Append(p.LastName);
            string x = sb.ToString();
         }) }
      };

      var Watch = new Stopwatch();
      foreach (var t in tests) {
         for (int i = 0; i < 5; i++) {
            Watch.Reset();
            long Elapsed = ElapsedTicks(t.Action, Watch, 10000);
            Console.WriteLine(string.Format("{0}: {1} ticks", t.Name, Elapsed.ToString()));
         }
      }
   }

   public static long ElapsedTicks(Action ActionDelg, Stopwatch Watch, int Iterations) {
      Watch.Start();
      for (int i = 0; i < Iterations; i++) {
         ActionDelg();
      }
      Watch.Stop();
      return Watch.ElapsedTicks / Iterations;
   }
}

نتائج:

Concat: 406 ticks
Concat: 356 ticks
Concat: 411 ticks
Concat: 299 ticks
Concat: 266 ticks
Format: 5269 ticks
Format: 954 ticks
Format: 1004 ticks
Format: 984 ticks
Format: 974 ticks
StringBuilder: 629 ticks
StringBuilder: 484 ticks
StringBuilder: 482 ticks
StringBuilder: 508 ticks
StringBuilder: 504 ticks

وفقًا لمادة MCSD الإعدادية، تقترح Microsoft استخدام عامل التشغيل + عند التعامل مع عدد صغير جدًا من التسلسلات (ربما من 2 إلى 4).ما زلت غير متأكد من السبب، ولكن هذا شيء يجب مراعاته.

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