ما الفرق بين String.Empty و"" (سلسلة فارغة)؟
-
02-07-2019 - |
سؤال
في .NET، ما هو الفرق بين String.Empty
و ""
, ، وهل هي قابلة للتبديل، أم أن هناك بعض المشكلات المرجعية أو التوطين الأساسية حول المساواة؟ String.Empty
سوف تضمن ليست مشكلة؟
المحلول
في .NET قبل الإصدار 2.0، ""
يخلق كائن بينما string.Empty
لا يخلق أي كائنالمرجع, ، مما يجعل string.Empty
أكثر فعالية.
في الإصدار 2.0 والإصدارات الأحدث من .NET، كافة تكرارات ""
الرجوع إلى نفس السلسلة الحرفية، وهو ما يعني ""
يعادل .Empty
, ، ولكن لا يزال ليس بالسرعة .Length == 0
.
.Length == 0
هو الخيار الأسرع، ولكن .Empty
يجعل رمزًا أنظف قليلاً.
نصائح أخرى
ما هو الفرق بين السلسلة.
string.Empty
هو حقل للقراءة فقط بينما ""
هو ثابت وقت الترجمة.الأماكن التي يتصرفون فيها بشكل مختلف هي:
قيمة المعلمة الافتراضية في C# 4.0 أو أعلى
void SomeMethod(int ID, string value = string.Empty)
// Error: Default parameter value for 'value' must be a compile-time constant
{
//... implementation
}
تعبير الحالة في بيان التبديل
string str = "";
switch(str)
{
case string.Empty: // Error: A constant value is expected.
break;
case "":
break;
}
وسيطات السمة
[Example(String.Empty)]
// Error: An attribute argument must be a constant expression, typeof expression
// or array creation expression of an attribute parameter type
كانت الإجابات السابقة صحيحة لـ .NET 1.1 (انظر إلى تاريخ المنشور الذي ربطوه:2003).اعتبارًا من .NET 2.0 والإصدارات الأحدث، لا يوجد فرق جوهريًا.سينتهي JIT بالإشارة إلى نفس الكائن الموجود في الكومة على أية حال.
وفقًا لمواصفات C#، القسم 2.4.4.5:http://msdn.microsoft.com/en-us/library/aa691090(VS.71).aspx
كل سلسلة حرفية لا تؤدي بالضرورة إلى نسخة سلسلة جديدة.عندما تظهر سلسلتان حرفيتان أو أكثر متكافئتان وفقًا لعامل مساواة السلسلة (القسم 7.9.7) في نفس التجميع، تشير هذه السلسلة الحرفية إلى نفس مثيل السلسلة.
حتى أن أحدهم ذكر هذا في تعليقات منشور براد أبرام
باختصار، النتيجة العملية لـ "" مقابل.String.Empty صفر.سوف يكتشف JIT ذلك في النهاية.
لقد وجدت شخصيًا أن JIT أكثر ذكاءً مني ولذلك أحاول ألا أكون ذكيًا جدًا في تحسينات المترجمات الدقيقة مثل هذا.سوف يتكشف JIT عن حلقات for()، ويزيل التعليمات البرمجية الزائدة، والأساليب المضمنة، وما إلى ذلك بشكل أفضل وفي أوقات أكثر ملاءمة مما توقعته أنا أو مترجم C# من قبل.دع JIT يقوم بعمله :)
String.Empty
هو يقرأ فقط المجال بينما ""
هو مقدار ثابت.هذا يعني أنه لا يمكنك استخدامه String.Empty
في بيان التبديل لأنه ليس ثابتا.
هناك اختلاف آخر وهو أن String.Empty ينشئ كود CIL أكبر.على الرغم من أن الكود المرجعي "" وString.Empty له نفس الطول، إلا أن المترجم لا يقوم بتحسين تسلسل السلاسل (راجع تعليمات إريك ليبرت مشاركة مدونة) للوسائط String.Empty.الوظائف المكافئة التالية
string foo()
{
return "foo" + "";
}
string bar()
{
return "bar" + string.Empty;
}
توليد هذا IL
.method private hidebysig instance string foo() cil managed
{
.maxstack 8
L_0000: ldstr "foo"
L_0005: ret
}
.method private hidebysig instance string bar() cil managed
{
.maxstack 8
L_0000: ldstr "bar"
L_0005: ldsfld string [mscorlib]System.String::Empty
L_000a: call string [mscorlib]System.String::Concat(string, string)
L_000f: ret
}
الإجابات المذكورة أعلاه صحيحة من الناحية الفنية، ولكن ما قد ترغب حقًا في استخدامه، للحصول على أفضل إمكانية قراءة التعليمات البرمجية وأقل فرصة للاستثناء هو String.IsNullOrEmpty(s)
أنا أميل إلى استخدام String.Empty
بدلا من ""
لسبب واحد بسيط ولكنه غير واضح:""
و ""
ليست متماثلة، فالأول يحتوي في الواقع على 16 حرفًا بعرض صفري.من الواضح أنه لن يقوم أي مطور مختص بوضع أحرف ذات عرض صفري في التعليمات البرمجية الخاصة به، ولكن إذا وصلوا إلى هناك، فقد يكون ذلك كابوسًا للصيانة.
ملحوظات:
إستعملت يو + فيف في هذا المثال.
لست متأكدًا مما إذا كانت SO ستأكل هذه الأحرف، لكن جرب ذلك بنفسك باستخدام أحد الأحرف العديدة ذات العرض الصفري
ولم أتوصل إلى هذا إلا بفضل https://codegolf.stackexchange.com/
يستخدم String.Empty
بدلا من ""
.
يعد هذا مفيدًا للسرعة أكثر من استخدام الذاكرة ولكنه نصيحة مفيدة.ال
""
هو حرفي لذلك سيكون بمثابة حرفي:عند الاستخدام الأول ، يتم إنشاءه وللاستخدامات التالية يتم إرجاع مرجعه.مثال واحد فقط من""
سيتم تخزينها في الذاكرة بغض النظر عن عدد المرات التي نستخدمها!لا أرى أي عقوبات الذاكرة هنا. المشكلة هي أنه في كل مرة""
يتم استخدام حلقة مقارنة للتحقق مما إذا كان""
موجود بالفعل في تجمع المتدربين. على الجانب الآخر،String.Empty
هي إشارة إلى أ""
المخزنة في منطقة ذاكرة .NET Framework.String.Empty
يشير إلى نفس عنوان الذاكرة لتطبيقات vb.net و c#.فلماذا تبحث عن مرجع في كل مرة تحتاج إليها""
عندما يكون لديك هذا المرجع فيString.Empty
?
مرجع: String.Empty
ضد ""
لا يقوم String.Empty بإنشاء كائن بينما يقوم "" بذلك.الفرق كما أشار هنا, ، ولكن تافهة.
كافة مثيلات "" هي نفسها، وهي عبارة عن سلسلة حرفية (أو ينبغي أن تكون كذلك).لذلك لن تقوم حقًا برمي كائن جديد على الكومة في كل مرة تستخدم فيها "" ولكن فقط تقوم بإنشاء مرجع لنفس الكائن الداخلي.بعد قولي هذا، أنا أفضل string.Empty.أعتقد أنه يجعل التعليمات البرمجية أكثر قابلية للقراءة.
string mystring = "";
ldstr ""
ldstr
يدفع مرجع كائن جديد إلى سلسلة حرفية مخزنة في بيانات التعريف.
string mystring = String.Empty;
ldsfld string [mscorlib]System.String::Empty
ldsfld
يدفع قيمة الحقل الثابت إلى مكدس التقييم
أنا أميل إلى استخدام String.Empty
بدلاً من ""
لأن IMHO أكثر وضوحًا وأقل VB-ish.
القادمة من وجهة نظر إطار الكيان:يبدو أن إصدارات EF 6.1.3 تتعامل مع String.Empty و"" بشكل مختلف عند التحقق من الصحة.
يتم التعامل مع string.Empty كقيمة فارغة لأغراض التحقق من الصحة وسوف يؤدي إلى خطأ في التحقق من الصحة إذا تم استخدامه في حقل مطلوب (منسوب)؛حيث "" سوف يجتاز التحقق من الصحة ولن يلقي الخطأ.
قد يتم حل هذه المشكلة في EF 7+.مرجع:- https://github.com/aspnet/EntityFramework/issues/2610 ).
يحرر:[Required(AllowEmptyStrings = true)] سوف يحل هذه المشكلة، مما يسمح للسلسلة.Empty بالتحقق من صحتها.
نظرًا لأن String.Empty ليس ثابتًا في وقت الترجمة، فلا يمكنك استخدامه كقيمة افتراضية في تعريف الدالة.
public void test(int i=0,string s="")
{
// Function Body
}
إريك ليبرت كتب (17 يونيو 2013):
"كانت الخوارزمية الأولى التي عملت عليها في برنامج التحويل البرمجي لـ C# هي المُحسِّن الذي يتعامل مع تسلسلات السلسلة. لسوء الحظ، لم أتمكن من نقل هذه التحسينات إلى قاعدة بيانات Roslyn قبل مغادرتي؛نأمل أن يصل شخص ما إلى ذلك!"
هنا بعض روزلين x64 النتائج اعتبارًا من يناير 2019.على الرغم من الملاحظات المتفق عليها للإجابات الأخرى في هذه الصفحة، لا يبدو لي أن x64 JIT الحالي يتعامل مع كل هذه الحالات بشكل مماثل، عندما يتم قول وفعل كل شيء.
ومع ذلك، لاحظ على وجه الخصوص أن واحدًا فقط من هذه الأمثلة ينتهي به الأمر بالاستدعاء String.Concat
, ، وأعتقد أن هذا لأسباب صحة غامضة (على عكس مراقبة التحسين).ويبدو أن الاختلافات الأخرى أصعب في التفسير.
default(String) + { default(String)، ""، String.Empty }
static String s00() => default(String) + default(String);
mov rax,[String::Empty]
mov rax,qword ptr [rax]
add rsp,28h
ret
static String s01() => default(String) + "";
mov rax,[String::Empty]
mov rax,qword ptr [rax]
add rsp,28h
ret
static String s02() => default(String) + String.Empty;
mov rax,[String::Empty]
mov rax,qword ptr [rax]
mov rdx,rax
test rdx,rdx
jne _L
mov rdx,rax
_L: mov rax,rdx
add rsp,28h
ret
"" + { default(String)، ""، String.Empty }
static String s03() => "" + default(String);
mov rax,[String::Empty]
mov rax,qword ptr [rax]
add rsp,28h
ret
static String s04() => "" + "";
mov rax,[String::Empty]
mov rax,qword ptr [rax]
add rsp,28h
ret
static String s05() => "" + String.Empty;
mov rax,[String::Empty]
mov rax,qword ptr [rax]
mov rdx,rax
test rdx,rdx
jne _L
mov rdx,rax
_L: mov rax,rdx
add rsp,28h
ret
String.Empty + { default(String)، ""، String.Empty }
static String s06() => String.Empty + default(String);
mov rax,[String::Empty]
mov rax,qword ptr [rax]
mov rdx,rax
test rdx,rdx
jne _L
mov rdx,rax
_L: mov rax,rdx
add rsp,28h
ret
static String s07() => String.Empty + "";
mov rax,[String::Empty]
mov rax,qword ptr [rax]
mov rdx,rax
test rdx,rdx
jne _L
mov rdx,rax
_L: mov rax,rdx
add rsp,28h
ret
static String s08() => String.Empty + String.Empty;
mov rcx,[String::Empty]
mov rcx,qword ptr [rcx]
mov qword ptr [rsp+20h],rcx
mov rcx,qword ptr [rsp+20h]
mov rdx,qword ptr [rsp+20h]
call F330CF60 ; <-- String.Concat
nop
add rsp,28h
ret
تفاصيل الاختبار
Microsoft (R) Visual C# Compiler version 2.10.0.0 (b9fb1610)
AMD64 Release
[MethodImpl(MethodImplOptions.NoInlining)]
'SuppressJitOptimization' = false
عندما تقوم بالمسح الضوئي عبر التعليمات البرمجية، تظهر "" ملونة بالطريقة التي يتم بها تلوين السلاسل.يبدو string.Empty وكأنه وصول عادي لأعضاء الفصل.خلال نظرة سريعة، من الأسهل اكتشاف "" أو استخلاص المعنى.
حدد السلاسل (لا يساعد تلوين تجاوز سعة المكدس تمامًا، ولكن في VS يكون هذا أكثر وضوحًا):
var i = 30;
var f = Math.Pi;
var s = "";
var d = 22.2m;
var t = "I am some text";
var e = string.Empty;
لقد قدم الجميع هنا بعض التوضيحات النظرية الجيدة.كان لدي شك مماثل.لذلك حاولت الترميز الأساسي عليه.ولقد وجدت الفرق.وهنا الفرق.
string str=null;
Console.WriteLine(str.Length); // Exception(NullRefernceException) for pointing to null reference.
string str = string.Empty;
Console.WriteLine(str.Length); // 0
لذلك يبدو أن كلمة "Null" تعني فارغة تمامًا وأن كلمة "String.Empty" تعني أنها تحتوي على نوع ما من القيمة، ولكنها فارغة.