Sự khác nhau trong các phương thức so sánh chuỗi trong C #


261

So sánh chuỗi trong C # khá đơn giản. Trong thực tế có một số cách để làm điều đó. Tôi đã liệt kê một số trong khối dưới đây. Điều tôi tò mò là sự khác biệt giữa chúng và khi nào nên sử dụng so với những cái khác? Có nên tránh bằng mọi giá? Có nhiều hơn tôi đã không được liệt kê?

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

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

(Lưu ý: Tôi đang tìm kiếm sự bình đẳng trong ví dụ này, không ít hơn hoặc lớn hơn nhưng cũng thoải mái nhận xét về điều đó)


4
Một cái bẫy là bạn không thể thực hiện chuỗiValue.Equals (null) vì giả sử bạn có thể gọi một phương thức trên null
johnc


@RobertHarvey Lý do tôi đến với stackoverflow là vì vậy tôi không phải đọc nhiều trang để trả lời.
Nizam Yahya Syaiful

@Syaiful: Lý do tôi đến với Stack Overflow là để tìm câu trả lời không có trong tài liệu.
Robert Harvey

Câu trả lời:


231

Dưới đây là các quy tắc về cách thức hoạt động của các chức năng này:

stringValue.CompareTo(otherStringValue)

  1. null đến trước một chuỗi
  2. nó sử dụng CultureInfo.CurrentCulture.CompareInfo.Compare, có nghĩa là nó sẽ sử dụng so sánh phụ thuộc vào văn hóa. Điều này có thể có nghĩa là ßsẽ so sánh bằng với SSở Đức hoặc tương tự

stringValue.Equals(otherStringValue)

  1. null không được coi là tương đương với bất cứ điều gì
  2. trừ khi bạn chỉ định một StringComparisontùy chọn, nó sẽ sử dụng cái trông giống như kiểm tra đẳng thức trực tiếp, tức ßlà không giống với SS, trong bất kỳ ngôn ngữ hoặc văn hóa nào

stringValue == otherStringValue

  1. Không giống như stringValue.Equals().
  2. Các ==nhà điều hành gọi là tĩnh Equals(string a, string b)phương pháp (mà lần lượt đi đến một bên EqualsHelperđể làm so sánh.
  3. Gọi .Equals()trên một nullchuỗi được nullngoại lệ tham chiếu, trong khi trên ==không.

Object.ReferenceEquals(stringValue, otherStringValue)

Chỉ cần kiểm tra xem các tham chiếu có giống nhau không, tức là nó không chỉ là hai chuỗi có cùng nội dung, bạn đang so sánh một đối tượng chuỗi với chính nó.


Lưu ý rằng với các tùy chọn ở trên sử dụng các cuộc gọi phương thức, sẽ có quá tải với nhiều tùy chọn hơn để chỉ định cách so sánh.

Lời khuyên của tôi nếu bạn chỉ muốn kiểm tra sự bình đẳng là hãy quyết định xem bạn có muốn sử dụng so sánh phụ thuộc vào văn hóa hay không, sau đó sử dụng .CompareTohoặc .Equals, tùy thuộc vào sự lựa chọn.


5
"stringValue.Equals (otherStringValue): null không bằng null" Lol, tôi sẽ nói là không. null bằng ngoại lệ ObjectReferenceNotset.
Kevin

29
== không giống như .Equals () ... Toán tử == gọi phương thức tĩnh Equals (chuỗi a, chuỗi b) (lần lượt đi đến EqualsHelper bên trong để thực hiện so sánh. Gọi .Equals trên null chuỗi được tham chiếu null, trong khi trên == thì không.
Dan C.

2
Mặt khác, .Equals nhanh hơn một chút (một phương thức gọi nội bộ ít hơn), nhưng ít đọc hơn - tất nhiên, có thể nói là :).
Dan C.

Tôi đã suy nghĩ '==' sẽ thực hiện so sánh tham chiếu và object.equals sẽ thực hiện so sánh giá trị. Làm thế nào '==' và string.equals hoạt động như nhau?
amesh

@ LasseV.Karlsen Ý kiến ​​của bạn về điều String.Comparegì?
JDandChips

72

Từ MSDN:

"Phương thức so sánh được thiết kế chủ yếu để sử dụng cho các hoạt động sắp xếp hoặc sắp xếp theo thứ tự chữ cái. Không nên sử dụng khi mục đích chính của lệnh gọi phương thức là xác định xem hai chuỗi có tương đương hay không. Để xác định xem hai chuỗi có tương đương hay không, hãy gọi phương thức Equals. "

Họ đề nghị sử dụng .Equalsthay vì .CompareTokhi chỉ tìm kiếm sự bình đẳng. Tôi không chắc chắn nếu có một sự khác biệt giữa .Equals==cho stringlớp học. Đôi khi tôi sẽ sử dụng .Equalshoặc Object.ReferenceEqualsthay vì ==cho các lớp của riêng mình trong trường hợp ai đó xuất hiện sau đó và xác định lại ==toán tử cho lớp đó.


18
Điều đó đã từng xảy ra với bạn? (Xác định lại ==) ... Tôi thấy đó là chương trình phòng thủ quá waaaay =)
juan

Vâng, đó là lý do tại sao bây giờ tôi sử dụng Object.ReferenceEquals khi tôi đang tìm kiếm sự bình đẳng đối tượng :). Nó có thể là một chút phòng thủ quá mức, nhưng tôi không điên cuồng về nó và thực sự tình huống này không bật lên rất thường xuyên.
Ed S.

Tôi nghi ngờ rằng 'mã hóa phòng thủ' này là hữu ích. Điều gì xảy ra nếu chủ sở hữu lớp cần ghi đè toán tử ==, sau đó phát hiện ra không có ai sử dụng nó?
Dave Van den Eynde

1
@DaveVandenEynde: Vâng ... Tôi đã viết lại điều này một lúc trước. Tôi không làm điều này thường xuyên, chỉ ghi đè .Equals khi thích hợp.
Ed S.

1
Khuyến nghị của Microsoft được ghi lại ở đây: Thực tiễn tốt nhất để sử dụng chuỗi trong .NET Framework
JJS

50

Nếu bạn từng tò mò về sự khác biệt trong các phương pháp BCL, Reflector là bạn của bạn :-)

Tôi làm theo các hướng dẫn sau:

Kết hợp chính xác: EDIT: Trước đây tôi luôn sử dụng toán tử == theo nguyên tắc bên trong Equals (chuỗi, chuỗi) toán tử object == được sử dụng để so sánh các tham chiếu đối tượng nhưng có vẻ như strA.Equals (strB) vẫn là 1-11% tổng thể nhanh hơn chuỗi.Equals (strA, strB), strA == strB và string.CompareOrdinal (strA, strB). Tôi đã kiểm tra vòng lặp với Đồng hồ bấm giờ trên cả hai giá trị chuỗi được thực hiện / không được thực hiện, với độ dài chuỗi giống nhau / khác nhau và kích thước khác nhau (1B đến 5MB).

strA.Equals(strB)

Trận đấu có thể đọc được của con người (văn hóa phương Tây, trường hợp không nhạy cảm):

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

Kết hợp có thể đọc được của con người (Tất cả các nền văn hóa khác, trường hợp không nhạy cảm / giọng nói / kana / etc được xác định bởi CultureInfo):

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

Phù hợp với con người có thể đọc được với các quy tắc tùy chỉnh (Tất cả các nền văn hóa khác):

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

18

Như Ed đã nói, so sánh được sử dụng để sắp xếp.

Tuy nhiên, có một sự khác biệt giữa .Equals và ==.

== giải quyết về cơ bản mã sau:

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

Lý do đơn giản là sau đây sẽ đưa ra một ngoại lệ:

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

bool equal = a.Equals(b);

Và sau đây sẽ không:

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

bool equal = a == b;

15

Có thể tìm thấy giải thích và thực hành tốt về các vấn đề so sánh chuỗi trong bài viết Các khuyến nghị mới về sử dụng chuỗi trong Microsoft .NET 2.0 và trong Thực tiễn tốt nhất để sử dụng chuỗi trong .NET Framework .


Mỗi phương pháp được đề cập (và khác) có mục đích cụ thể. Sự khác biệt chính giữa chúng là loại liệt kê StringComparison mà chúng đang sử dụng theo mặc định. Có một số lựa chọn:

  • Văn hóa hiện tại
  • Hiện tạiCARMIgnoreCase
  • Bất biến
  • InvariantCARMIgnoreCase
  • Bình thường
  • OrdinalIgnoreCase

Mỗi loại so sánh ở trên mục tiêu trường hợp sử dụng khác nhau:

  • Bình thường
    • Định danh nội bộ phân biệt chữ hoa chữ thường
    • Các định danh phân biệt chữ hoa chữ thường trong các tiêu chuẩn như XML và HTTP
    • Cài đặt liên quan đến bảo mật phân biệt chữ hoa chữ thường
  • OrdinalIgnoreCase
    • Định danh nội bộ không phân biệt chữ hoa chữ thường
    • Các định danh không phân biệt chữ hoa chữ thường trong các tiêu chuẩn như XML và HTTP
    • Đường dẫn tệp (trên Microsoft Windows)
    • Khóa / giá trị đăng ký
    • Biến môi trường
    • Mã định danh tài nguyên (ví dụ xử lý tên)
    • Trường hợp cài đặt liên quan đến bảo mật không nhạy cảm
  • InvariantCARM hoặc InvariantCARMIgnoreCase
    • Một số dữ liệu liên quan đến ngôn ngữ
    • Hiển thị dữ liệu ngôn ngữ yêu cầu một thứ tự sắp xếp cố định
  • Hiện tại Văn hóa hoặc Hiện tại Văn hóaIgnoreCase
    • Dữ liệu được hiển thị cho người dùng
    • Đầu vào người dùng nhiều nhất

Lưu ý, Bảng liệt kê StringComparison cũng như quá tải cho các phương thức so sánh chuỗi, tồn tại kể từ .NET 2.0.


Phương thức String.CompareTo (Chuỗi)

Trên thực tế, loại thực thi an toàn của Phương thức IComparable.CompareTo . Giải thích mặc định: CurrentCARM.

Sử dụng:

Phương thức so sánh được thiết kế chủ yếu để sử dụng trong các hoạt động sắp xếp hoặc bảng chữ cái

Như vậy

Việc triển khai giao diện IComparable sẽ nhất thiết phải sử dụng phương pháp này

Phương thức String.Compare

Một thành viên tĩnh của String Class có nhiều tình trạng quá tải. Giải thích mặc định: CurrentCARM.

Bất cứ khi nào có thể, bạn nên gọi quá tải phương thức So sánh bao gồm tham số StringComparison.

Phương thức String.Equals

Overriden từ lớp Object và quá tải cho loại an toàn. Giải thích mặc định: Thông thường. Thông báo rằng:

Các phương thức đẳng thức của lớp String bao gồm Equals tĩnh , toán tử tĩnh ==phương thức thể hiện bằng .


Lớp StringComparer

Ngoài ra còn có một cách khác để đối phó với các so sánh chuỗi, đặc biệt là nhằm sắp xếp:

Bạn có thể sử dụng lớp StringComparer để tạo một so sánh cụ thể theo kiểu để sắp xếp các phần tử trong một bộ sưu tập chung. Các lớp như Hashtable, Dictionary, SortedList và SortedList sử dụng lớp StringComparer cho mục đích sắp xếp.


2
Theo một số bài đăng khác trên SO, tất cả các phương pháp khác với phương pháp thứ tự đều có trường hợp So sánh (a, b) và So sánh (b, a) đều có thể trả về 1 và lỗi đã được phân loại là "sẽ không được sửa ". Như vậy, tôi không chắc bất kỳ so sánh như vậy có bất kỳ trường hợp sử dụng.
supercat

@supercat bạn có thể liên kết đến đó, hoặc đưa ra một ví dụ?
Noctis

1
Xem stackoverflow.com/questions/17599084/ cấp để thảo luận về vấn đề này.
supercat

7

Không phải hiệu suất thường có vấn đề với 99% số lần bạn cần làm điều này, nhưng nếu bạn phải thực hiện việc này trong một vòng lặp vài triệu lần, tôi rất khuyến nghị bạn nên sử dụng .Equals hoặc == vì ngay khi tìm thấy ký tự không phù hợp, nó sẽ ném toàn bộ thành sai, nhưng nếu bạn sử dụng So sánh, nó sẽ phải tìm ra nhân vật nào ít hơn nhân vật kia, dẫn đến thời gian thực hiện kém hơn một chút.

Nếu ứng dụng của bạn sẽ chạy ở các quốc gia khác nhau, tôi khuyên bạn nên xem ý nghĩa của CultureInfo và có thể sử dụng .Equals. Vì tôi chỉ thực sự viết ứng dụng cho Hoa Kỳ (và không quan tâm nếu nó không hoạt động đúng bởi ai đó), tôi luôn chỉ sử dụng ==.


5

Trong các hình thức bạn liệt kê ở đây, không có nhiều sự khác biệt giữa hai hình thức. CompareTokết thúc bằng cách gọi một CompareInfophương thức thực hiện so sánh bằng văn hóa hiện tại; Equalsđược gọi bởi ==toán tử.

Nếu bạn xem xét quá tải, thì mọi thứ sẽ khác đi. Compare==chỉ có thể sử dụng văn hóa hiện tại để so sánh một chuỗi. EqualsString.Comparecó thể đưa ra một StringComparisonđối số liệt kê cho phép bạn chỉ định các so sánh không nhạy cảm về văn hóa hoặc không phân biệt chữ hoa chữ thường. Chỉ String.Comparecho phép bạn chỉ định a CultureInfovà thực hiện so sánh bằng cách sử dụng văn hóa khác với văn hóa mặc định.

Vì tính linh hoạt của nó, tôi thấy tôi sử dụng String.Comparenhiều hơn bất kỳ phương pháp so sánh nào khác; nó cho phép tôi xác định chính xác những gì tôi muốn.


2

Một điểm khác biệt LỚN cần lưu ý là .Equals () sẽ đưa ra một ngoại lệ nếu chuỗi đầu tiên là null, trong khi đó == sẽ không.

       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): KHÔNG sử dụng nếu mục đích chính là xác định xem hai chuỗi có tương đương không
  • s1 == s2: Không thể bỏ qua trường hợp
  • s1.Equals (s2, StringComparison): Ném NullReferenceException nếu s1 là null
  • String.Equals (s2, StringComparison): Theo quá trình loại bỏ, phương thức tĩnh này là CHIẾN THẮNG (giả sử trường hợp sử dụng điển hình để xác định xem hai chuỗi có tương đương không)!


-9

với .Equals, bạn cũng có được các tùy chọn StringComparison. rất tiện dụng để bỏ qua trường hợp và những thứ khác.

btw, điều này sẽ đánh giá thành sai

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

return a==b

Vì == so sánh các giá trị của a và b (là các con trỏ) nên điều này sẽ chỉ đánh giá là đúng nếu các con trỏ trỏ đến cùng một đối tượng trong bộ nhớ. .Equals hủy bỏ các con trỏ và so sánh các giá trị được lưu trữ tại các con trỏ. a.Equals (b) sẽ đúng ở đây.

và nếu bạn thay đổi b thành:

b = "MYSTRING";

thì a.Equals (b) là sai, nhưng

a.Equals(b, StringComparison.OrdinalIgnoreCase) 

sẽ là sự thật

a.CompareTo (b) gọi hàm so sánh của chuỗi so sánh các giá trị tại các con trỏ và trả về <0 nếu giá trị được lưu trữ tại a nhỏ hơn giá trị được lưu trữ tại b, trả về 0 nếu a.Equals (b) là đúng và > 0 nếu không. Tuy nhiên, đây là trường hợp nhạy cảm, tôi nghĩ có thể có các tùy chọn để So sánh để bỏ qua trường hợp và như vậy, nhưng không có thời gian để xem xét ngay bây giờ. Như những người khác đã nêu, điều này sẽ được thực hiện để sắp xếp. So sánh cho sự bình đẳng theo cách này sẽ dẫn đến chi phí không cần thiết.

Tôi chắc chắn rằng tôi sẽ bỏ qua mọi thứ, nhưng tôi nghĩ đây là thông tin đủ để bắt đầu thử nghiệm nếu bạn cần thêm chi tiết.


9
Phần a == b không chính xác. Toán tử == bị quá tải một cách hiệu quả cho lớp String và nó so sánh các giá trị bất kể các tham chiếu thực tế.
Goyuix
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.