ความแตกต่างในวิธีการเปรียบเทียบสตริงใน C #


261

การเปรียบเทียบสตริงใน C # นั้นค่อนข้างง่าย ในความเป็นจริงมีหลายวิธีที่จะทำ ฉันได้ระบุไว้บางส่วนในบล็อกด้านล่าง สิ่งที่ฉันอยากรู้เกี่ยวกับความแตกต่างระหว่างพวกเขาและเมื่อหนึ่งควรจะใช้กับคนอื่น ๆ ? ควรหลีกเลี่ยงค่าใช้จ่ายทั้งหมดหรือไม่ มีมากกว่าที่ฉันยังไม่ได้ระบุไว้?

string testString = "Test";
string anotherString = "Another";

if (testString.CompareTo(anotherString) == 0) {}
if (testString.Equals(anotherString)) {}
if (testString == anotherString) {}

(หมายเหตุ: ฉันกำลังมองหาความเสมอภาคในตัวอย่างนี้ไม่น้อยกว่าหรือมากกว่า แต่อย่าลังเลที่จะแสดงความคิดเห็นในเรื่องนั้นด้วย)


4
หนึ่งคือกับดักที่คุณไม่สามารถทำ stringValue.Equals (null) เป็นที่ถือว่าคุณสามารถโทรหาวิธีการในการโมฆะ
johnc


@RobertHarvey เหตุผลที่ฉันมา stackoverflow ก็คือฉันไม่ต้องอ่านหลาย ๆ หน้าเพื่อหาคำตอบ
Syaiful Nizam Yahya

@Syaiful: เหตุผลที่ฉันมาที่ Stack Overflow ก็เพื่อค้นหาคำตอบที่ไม่ได้อยู่ในเอกสาร
Robert Harvey

คำตอบ:


231

นี่คือกฎสำหรับการทำงานของฟังก์ชันเหล่านี้:

stringValue.CompareTo(otherStringValue)

  1. null มาก่อนสตริง
  2. มันใช้CultureInfo.CurrentCulture.CompareInfo.Compareซึ่งหมายความว่ามันจะใช้การเปรียบเทียบแบบพึ่งพาวัฒนธรรม นี่อาจหมายถึงว่าßจะเปรียบเทียบกับSSในเยอรมนีหรือใกล้เคียงกัน

stringValue.Equals(otherStringValue)

  1. null ไม่ถือว่าเท่ากับอะไร
  2. นอกจากว่าคุณจะระบุStringComparisonตัวเลือกมันจะใช้สิ่งที่ดูเหมือนว่าการตรวจสอบความเท่าเทียมลำดับโดยตรงßคือไม่เหมือนกับSSในภาษาหรือวัฒนธรรมใด ๆ

stringValue == otherStringValue

  1. stringValue.Equals()ไม่ได้เป็นเช่นเดียวกับ
  2. ==ผู้ประกอบการเรียกร้องให้คงEquals(string a, string b)วิธีการ (ซึ่งในทางกลับไปภายในEqualsHelperจะทำเปรียบเทียบ
  3. การเรียก.Equals()ใช้nullสตริงได้รับnullการยกเว้นในขณะที่เปิด==ไม่ได้

Object.ReferenceEquals(stringValue, otherStringValue)

เพียงตรวจสอบว่าการอ้างอิงเหมือนกันนั่นคือไม่ใช่แค่สองสายที่มีเนื้อหาเดียวกันคุณกำลังเปรียบเทียบวัตถุสตริงกับตัวเอง


โปรดทราบว่าด้วยตัวเลือกด้านบนที่ใช้การเรียกใช้เมธอดจะมีโอเวอร์โหลดที่มีตัวเลือกเพิ่มเติมเพื่อระบุวิธีเปรียบเทียบ

คำแนะนำของฉันหากคุณต้องการตรวจสอบความเท่าเทียมกันคือตัดสินใจว่าคุณต้องการใช้การเปรียบเทียบแบบพึ่งพาวัฒนธรรมหรือไม่จากนั้นใช้.CompareToหรือ.Equalsขึ้นอยู่กับตัวเลือก


5
"stringValue.Equals (otherStringValue): null ไม่เท่ากับ null" Lol ฉันจะไม่พูด null เท่ากับข้อยกเว้น ObjectReferenceNotSet
เควิน

29
== ไม่เหมือนกันกับ. Equals () ... ตัวดำเนินการ == เรียกใช้เมธอด Equals (string a, string b) แบบคงที่ (ซึ่งจะไปที่ EqualsHelper ภายในเพื่อทำการเปรียบเทียบการโทร .Equals บน null สตริงได้รับการอ้างอิงเป็นโมฆะ null ขณะที่ == ไม่
Dan C.

2
ในทางกลับกัน .Equals เร็วขึ้นเล็กน้อย (การเรียกใช้เมธอดน้อยกว่าภายใน) แต่สามารถอ่านได้น้อยกว่า - แน่นอนว่า :)
Dan C.

ฉันคิดว่า '==' จะทำการเปรียบเทียบการอ้างอิงและ object.equals จะทำการเปรียบเทียบค่าอย่างไร '==' และ string.equals ทำงานเหมือนกันหรือไม่
amesh

@ LasseV.Karlsen คุณมีความคิดเห็นString.Compareอย่างไร?
JDandChips

72

จาก MSDN:

"วิธี CompareTo ได้รับการออกแบบมาเป็นหลักสำหรับใช้ในการเรียงลำดับหรือการดำเนินการเรียงตามตัวอักษรมันไม่ควรใช้เมื่อวัตถุประสงค์หลักของการเรียกใช้เมธอดคือการตรวจสอบว่ามีสองสายที่เทียบเท่ากันหรือไม่ "

พวกเขาแนะนำให้ใช้.Equalsแทนที่จะ.CompareToมองหาความเท่าเทียมกัน ฉันไม่แน่ใจว่ามีความแตกต่างระหว่าง.Equalsและ==สำหรับstringชั้นเรียนหรือไม่ บางครั้งฉันจะใช้.EqualsหรือObject.ReferenceEqualsแทน==ชั้นเรียนของฉันเองในกรณีที่มีใครบางคนเข้ามาทีหลังและกำหนดตัว==ดำเนินการสำหรับคลาสนั้นใหม่


18
นั่นเคยเกิดขึ้นกับคุณหรือไม่? (Redefining ==) ... ผมเห็นว่ามันเป็น waaaay การเขียนโปรแกรมป้องกันเกินไป =)

ใช่นั่นคือเหตุผลที่ฉันใช้ Object.ReferenceEquals ตอนที่ฉันกำลังมองหา object equality :) มันอาจจะเป็นการป้องกันแบบตาด แต่ฉันไม่ใช่คนคลั่งไคล้และจริง ๆ แล้วสถานการณ์นี้ไม่ได้ปรากฏขึ้นบ่อยนัก
Ed S.

ฉันสงสัยว่า 'การป้องกันแบบเข้ารหัส' นี้มีประโยชน์หรือไม่ ถ้าเจ้าของคลาสต้องการแทนที่โอเปอเรเตอร์ == ดังนั้นจะไม่มีใครใช้มัน?
Dave Van den Eynde

1
@DaveVandenEynde: ใช่ ... ฉันเขียนมันย้อนกลับไป ฉันไม่ได้ทำเช่นนี้เป็นประจำเพียงเอาชนะ. เหมาะสมเมื่อเหมาะสม
Ed S.

1
มีการบันทึกคำแนะนำของ Microsoft ไว้ที่นี่: แนวทางปฏิบัติที่ดีที่สุดสำหรับการใช้สตริงใน. NET Framework
JJS

50

หากคุณเคยสงสัยเกี่ยวกับความแตกต่างในวิธีการ BCL Reflectorคือเพื่อนของคุณ :-)

ฉันทำตามคำแนะนำเหล่านี้:

การจับคู่แบบตรงทั้งหมด: แก้ไข: ก่อนหน้านี้ฉันเคยใช้ตัวดำเนินการ == ตามหลักการที่ว่าข้างในเท่ากับ (สตริงสตริง) ตัวดำเนินการวัตถุ == ใช้เพื่อเปรียบเทียบการอ้างอิงวัตถุ แต่ดูเหมือนว่า strA.Equals (strB) ยังคงเป็น 1-11% โดยรวมเร็วกว่า string.Equals (strA, strB), strA == strB และ string.CompareOrdinal (strA, strB) ฉันวนลูปที่ทดสอบด้วย StopWatch บนค่าสตริงทั้งแบบ interned / non-interned ที่มีความยาวสตริงเดียวกัน / แตกต่างกันและขนาดที่แตกต่างกัน (1B ถึง 5MB)

strA.Equals(strB)

การจับคู่ที่มนุษย์อ่านได้ (วัฒนธรรมตะวันตก, ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่):

string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase) == 0

การจับคู่ที่มนุษย์สามารถอ่านได้ (วัฒนธรรมอื่น ๆ ทั้งหมดตัวพิมพ์เล็ก / ใหญ่ / สำเนียง / คานา / อื่น ๆ ที่กำหนดโดย CultureInfo):

string.Compare(strA, strB, myCultureInfo) == 0

การจับคู่ที่มนุษย์สามารถอ่านได้ด้วยกฎที่กำหนดเอง (วัฒนธรรมอื่น ๆ ทั้งหมด):

CompareOptions compareOptions = CompareOptions.IgnoreCase
                              | CompareOptions.IgnoreWidth
                              | CompareOptions.IgnoreNonSpace;
string.Compare(strA, strB, CultureInfo.CurrentCulture, compareOptions) == 0

18

อย่างที่Edกล่าวว่า CompareTo ใช้สำหรับการจัดเรียง

อย่างไรก็ตามมีความแตกต่างระหว่าง. Qual และ ==

== แก้ไขเป็นหลักรหัสต่อไปนี้:

if(object.ReferenceEquals(left, null) && 
   object.ReferenceEquals(right, null))
    return true;
if(object.ReferenceEquals(left, null))
    return right.Equals(left);
return left.Equals(right);

เหตุผลง่ายๆคือสิ่งต่อไปนี้จะทำให้เกิดข้อยกเว้น:

string a = null;
string b = "foo";

bool equal = a.Equals(b);

และสิ่งต่อไปนี้จะไม่:

string a = null;
string b = "foo";

bool equal = a == b;

15

คำอธิบายที่ดีและการปฏิบัติเกี่ยวกับปัญหาการเปรียบเทียบสตริงอาจพบได้ในบทความคำแนะนำใหม่สำหรับการใช้สายใน Microsoft .NET 2.0และยังอยู่ในการปฏิบัติที่ดีที่สุดสำหรับการใช้สายใน .NET Framework


แต่ละวิธีที่กล่าวถึง (และอื่น ๆ ) มีวัตถุประสงค์เฉพาะ ความแตกต่างที่สำคัญระหว่างพวกเขาคือการเรียงลำดับของStringComparison แจงนับที่พวกเขาใช้เป็นค่าเริ่มต้น มีหลายตัวเลือก:

  • CurrentCulture
  • CurrentCultureIgnoreCase
  • InvariantCulture
  • InvariantCultureIgnoreCase
  • เกี่ยวกับลำดับ
  • OrdinalIgnoreCase

แต่ละประเภทการเปรียบเทียบข้างต้นมีเป้าหมายการใช้งานที่แตกต่างกัน:

  • เกี่ยวกับลำดับ
    • ตัวบ่งชี้ภายในที่คำนึงถึงขนาดตัวพิมพ์
    • ตัวบ่งชี้ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ในมาตรฐานเช่น XML และ HTTP
    • การตั้งค่าที่เกี่ยวข้องกับความปลอดภัย
  • OrdinalIgnoreCase
    • ตัวบ่งชี้ภายในที่ไม่คำนึงถึงขนาดตัวพิมพ์
    • ตัวบ่งชี้ตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ในมาตรฐานเช่น XML และ HTTP
    • เส้นทางไฟล์ (บน Microsoft Windows)
    • คีย์รีจิสทรี / ค่า
    • ตัวแปรสภาพแวดล้อม
    • ตัวระบุทรัพยากร (ตัวอย่างเช่นจัดการชื่อ)
    • การตั้งค่าที่เกี่ยวข้องกับความปลอดภัย
  • InvariantCulture หรือ InvariantCultureIgnoreCase
    • ข้อมูลบางอย่างที่เกี่ยวข้องกับภาษายังคงอยู่
    • การแสดงข้อมูลภาษาศาสตร์ที่ต้องการเรียงลำดับแบบคงที่
  • CurrentCulture หรือ CurrentCultureIgnoreCase
    • ข้อมูลที่แสดงให้กับผู้ใช้
    • อินพุตผู้ใช้ส่วนใหญ่

โปรดสังเกตว่าStringComparison Enumerationและ Overloads สำหรับวิธีการเปรียบเทียบสตริงมีอยู่ตั้งแต่. NET 2.0


String.CompareTo วิธี (String)

อยู่ในความเป็นจริงประเภทการใช้งานที่ปลอดภัยของIComparable วิธีเปรียบเทียบกับ การตีความเริ่มต้น: CurrentCulture

การใช้งาน:

เมธอด CompareTo ได้รับการออกแบบมาเพื่อใช้ในการเรียงลำดับหรือเรียงลำดับอักษรเป็นหลัก

ดังนั้น

การใช้อินเทอร์เฟซ IComparable จะจำเป็นต้องใช้วิธีนี้

String.Compare วิธี

สมาชิกแบบคงที่ของString Classซึ่งมีจำนวนมาก การตีความเริ่มต้น: CurrentCulture

เมื่อใดก็ตามที่เป็นไปได้คุณควรเรียกโอเวอร์โหลดของวิธีการเปรียบเทียบที่มีพารามิเตอร์ StringComparison

String.Equals วิธี

Overriden จาก Object class และโอเวอร์โหลดเพื่อความปลอดภัยของประเภท การตีความเริ่มต้น: ลำดับ สังเกตว่า:

วิธีการเสมอภาค String ระดับ ได้แก่แบบคงที่เท่ากับที่ผู้ประกอบการคง ==และเท่ากับวิธีการเช่น


คลาส StringComparer

นอกจากนี้ยังมีวิธีอื่นในการจัดการกับการเปรียบเทียบสตริงโดยเฉพาะอย่างยิ่งมีเป้าหมายเพื่อการเรียงลำดับ:

คุณสามารถใช้คลาส StringComparerเพื่อสร้างการเปรียบเทียบเฉพาะชนิดเพื่อเรียงลำดับองค์ประกอบในคอลเลกชันทั่วไป คลาสเช่น Hashtable, Dictionary, SortedList และ SortedList ใช้คลาส StringComparer สำหรับการเรียงลำดับ


2
ตามโพสต์อื่น ๆ ใน SO วิธีการอื่น ๆ นอกจากลำดับที่มีกรณีที่เปรียบเทียบ (a, b) และเปรียบเทียบ (b, a) สามารถกลับ 1 และข้อผิดพลาดได้รับการจัดประเภทเป็น "จะไม่คงที่ " เป็นเช่นนี้ผมไม่แน่ใจว่าการเปรียบเทียบดังกล่าวมีใด ๆกรณีการใช้งาน
supercat

@supercat คุณสามารถลิงค์ไปยังสิ่งนั้นได้หรือยกตัวอย่าง
Noctis

1
ดูstackoverflow.com/questions/17599084/…สำหรับการอภิปรายปัญหา
supercat

7

ไม่ใช่ว่าการแสดงนั้นมักจะเกี่ยวข้องกับ 99% ของเวลาที่คุณต้องทำ แต่ถ้าคุณต้องทำแบบวนซ้ำหลายล้านครั้งฉันจะขอแนะนำให้คุณใช้. Equal หรือ == เพราะทันทีที่พบตัวละคร นั่นไม่ตรงกับที่มันจะทำให้สิ่งต่าง ๆ เป็นเท็จ แต่ถ้าคุณใช้ CompareTo มันจะต้องคิดออกว่าตัวละครตัวไหนดีกว่าตัวอื่น ๆ ทำให้เวลาในการแสดงแย่ลงเล็กน้อย

หากแอปของคุณกำลังทำงานในประเทศต่าง ๆ ฉันขอแนะนำให้คุณดูความหมายของ CultureInfo และอาจใช้ เนื่องจากฉันเขียนแอพสำหรับสหรัฐอเมริกาเท่านั้น (และไม่สนใจว่าบางคนทำงานไม่ถูกต้อง) ฉันจึงใช้ ==


5

ในแบบฟอร์มที่คุณระบุไว้ที่นี่ไม่มีความแตกต่างระหว่างทั้งสองอย่างมาก CompareToจบลงด้วยการเรียกCompareInfoวิธีการที่เปรียบเทียบโดยใช้วัฒนธรรมปัจจุบัน Equalsถูกเรียกโดย==ผู้ประกอบการ

หากคุณพิจารณาการโอเวอร์โหลดสิ่งต่าง ๆ จะแตกต่างกัน Compareและ==สามารถใช้วัฒนธรรมปัจจุบันเพื่อเปรียบเทียบสตริงเท่านั้น EqualsและString.Compareสามารถใช้StringComparisonอาร์กิวเมนต์การแจงนับที่ให้คุณระบุการเปรียบเทียบแบบคำนึงถึงวัฒนธรรมหรือแบบตัวเล็ก เพียง แต่String.Compareช่วยให้คุณสามารถระบุCultureInfoและดำเนินการเปรียบเทียบโดยใช้วัฒนธรรมอื่นที่ไม่ใช่วัฒนธรรมเริ่มต้น

เนื่องจากความเก่งกาจของมันฉันพบว่าฉันใช้String.Compareมากกว่าวิธีเปรียบเทียบอื่น ๆ มันช่วยให้ฉันระบุสิ่งที่ฉันต้องการได้อย่างแน่นอน


2

ความแตกต่างที่สำคัญอย่างหนึ่งที่ควรทราบคือ .qual () จะทำให้เกิดข้อยกเว้นหากสตริงแรกเป็นโมฆะโดยที่ == จะไม่เกิดขึ้น

       string s = null;
        string a = "a";
        //Throws {"Object reference not set to an instance of an object."}
        if (s.Equals(a))
            Console.WriteLine("s is equal to a");
        //no Exception
        if(s==a)
            Console.WriteLine("s is equal to a");

0
  • s1.CompareTo (s2): อย่าใช้ถ้าจุดประสงค์หลักคือการตรวจสอบว่าสองสายจะเทียบเท่า
  • s1 == s2: ไม่สามารถเพิกเฉยกรณีและปัญหา
  • s1.Equals (s2, StringComparison): ส่ง NullReferenceException ถ้า s1 เป็นโมฆะ
  • String.Equals (s2, StringComparison): โดยกระบวนการกำจัดวิธีแบบคงที่นี้คือผู้ชนะ (สมมติว่าเป็นกรณีการใช้งานทั่วไปเพื่อพิจารณาว่าสองสายจะเทียบเท่า)!


-9

ด้วย. Equals คุณจะได้รับตัวเลือก StringComparison มีประโยชน์มากสำหรับกรณีที่ไม่สนใจและสิ่งอื่น ๆ

btw สิ่งนี้จะประเมินเป็นเท็จ

string a = "myString";
string b = "myString";

return a==b

เนื่องจาก == เปรียบเทียบค่าของ a และ b (ซึ่งเป็นพอยน์เตอร์) สิ่งนี้จะประเมินเป็นจริงหากพอยน์เตอร์ชี้ไปที่วัตถุเดียวกันในหน่วยความจำ .Equals dereferences ตัวชี้และเปรียบเทียบค่าที่เก็บไว้ที่ตัวชี้ a.Equals (b) จะเป็นจริงที่นี่

และถ้าคุณเปลี่ยน b เป็น:

b = "MYSTRING";

a.Equals (b) เป็นเท็จ แต่

a.Equals(b, StringComparison.OrdinalIgnoreCase) 

จะเป็นจริง

a.CompareTo (b) เรียกใช้ฟังก์ชัน CompareTo ของสตริงซึ่งเปรียบเทียบค่าที่ตัวชี้และส่งกลับ <0 หากค่าที่เก็บที่ a น้อยกว่าค่าที่เก็บไว้ที่ b ส่งคืน 0 ถ้า a.Equals (b) เป็นจริงและ > 0 มิฉะนั้น อย่างไรก็ตามนี่เป็นแบบตรงตามตัวพิมพ์เล็ก - ใหญ่ฉันคิดว่าอาจมีตัวเลือกต่าง ๆ สำหรับ CompareTo ในการเพิกเฉยตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ แต่ไม่มีเวลาดูเลย ตามที่คนอื่น ๆ ได้ระบุไว้แล้วสิ่งนี้จะทำเพื่อการเรียงลำดับ การเปรียบเทียบความเสมอภาคในลักษณะนี้จะส่งผลให้เกิดค่าใช้จ่ายที่ไม่จำเป็น

ฉันแน่ใจว่าฉันกำลังละทิ้ง แต่ฉันคิดว่านี่ควรเป็นข้อมูลที่เพียงพอที่จะเริ่มการทดสอบหากคุณต้องการรายละเอียดเพิ่มเติม


9
ส่วน a == b ไม่ถูกต้อง ตัวดำเนินการ == โอเวอร์โหลดอย่างมีประสิทธิภาพสำหรับคลาส String และเปรียบเทียบค่าโดยไม่คำนึงถึงการอ้างอิงจริง
Goyuix
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.