簡體   English   中英

C# == 和 Equals() 之間的區別

[英]C# difference between == and Equals()

我在比較 2 個字符串的 silverlight 應用程序中有一個條件,由於某種原因,當我使用==時它返回false.Equals()返回true

這是代碼:

if (((ListBoxItem)lstBaseMenu.SelectedItem).Content.Equals("Energy Attack"))
{
    // Execute code
}

if (((ListBoxItem)lstBaseMenu.SelectedItem).Content == "Energy Attack")
{
    // Execute code
}

為什么會發生這種情況?

==用於object類型的表達式時,它將解析為System.Object.ReferenceEquals

Equals只是一個virtual方法並且行為如此,因此將使用覆蓋的版本(對於string類型比較內容)。

將對象引用與字符串進行比較時(即使對象引用引用字符串),特定於字符串類的==運算符的特殊行為將被忽略。

通常(即不處理字符串時), Equals比較,而==比較對象引用 如果您比較的兩個對象指的是一個對象的同一個實例,則兩者都將返回 true,但如果一個具有相同的內容並且來自不同的源(是具有相同數據的單獨實例),則只有 Equals返回真。 但是,正如注釋中所指出的,字符串是一種特殊情況,因為它覆蓋了==運算符,因此在純粹處理字符串引用(而不是對象引用)時,即使它們是單獨的實例,也只會比較值。 以下代碼說明了行為的細微差別:

string s1 = "test";
string s2 = "test";
string s3 = "test1".Substring(0, 4);
object s4 = s3;

Console.WriteLine($"{object.ReferenceEquals(s1, s2)} {s1 == s2} {s1.Equals(s2)}");
Console.WriteLine($"{object.ReferenceEquals(s1, s3)} {s1 == s3} {s1.Equals(s3)}");
Console.WriteLine($"{object.ReferenceEquals(s1, s4)} {s1 == s4} {s1.Equals(s4)}");

輸出是:

True True True
False True True
False False True

==.Equals都取決於實際類型中定義的行為和調用站點的實際類型。 兩者都只是方法/運算符,可以在任何類型上覆蓋並給出作者想要的任何行為。 根據我的經驗,我發現人們在對象上實現.Equals而忽略實現 operator ==是很常見的。 這意味着.Equals將實際測量值的相等性,而==將測量它們是否是相同的引用。

當我使用定義不斷變化的新類型或編寫泛型算法時,我發現最佳實踐如下

  • 如果我想比較 C# 中的引用,我直接使用Object.ReferenceEquals (一般情況下不需要)
  • 如果我想比較值,我使用EqualityComparer<T>.Default

在某些情況下,當我覺得==的用法有歧義時,我會在代碼中明確使用Object.Reference equals 來消除歧義。

Eric Lippert 最近發表了一篇關於為什么 CLR 中有 2 種相等方法的主題的博客文章。 值得一讀

== 運算符

  1. 如果操作數是值類型並且它們的相等,則返回 true 否則返回 false。
  2. 如果操作數是除字符串之外的引用類型並且都引用同一個實例(同一個對象),則返回 true 否則返回 false。
  3. 如果操作數是字符串類型並且它們的相等,則返回真,否則返回假。

。等於

  1. 如果操作數是引用類型,則執行引用相等,即如果兩者都引用同一個實例(同一個對象),則返回 true,否則返回 false。
  2. 如果操作數是值類型,則與 == 運算符不同,它首先檢查它們的類型,如果它們的類型相同,則執行 == 運算符,否則返回 false。

據我了解,答案很簡單:

  1. ==比較對象引用。
  2. .Equals比較對象內容。
  3. String數據類型總是像內容比較一樣。

我希望我是正確的,它回答了你的問題。

首先,有區別的。 對於數字

> 2 == 2.0
True

> 2.Equals(2.0)
False

而對於字符串

> string x = null;
> x == null
True

> x.Equals(null)
NullReferenceException

在這兩種情況下, ==.Equals

我要補充一點,如果您將對象轉換為字符串,那么它將正常工作。 這就是為什么編譯器會給你一個警告說:

可能的意外參考比較; 要進行值比較,請將左側強制轉換為“字符串”

因為到目前為止還沒有提到.Equal方法的靜態版本,我想在這里添加這個來總結和比較 3 個變體。

MyString.Equals("Somestring"))          //Method 1
MyString == "Somestring"                //Method 2
String.Equals("Somestring", MyString);  //Method 3 (static String.Equals method) - better

其中MyString是來自代碼中其他地方的變量。

背景信息和總結:

在 Java 中,不應使用==來比較字符串。 如果您需要同時使用這兩種語言,我會提到這一點,並且讓您知道使用==也可以用 C# 中更好的東西代替。

在 C# 中,使用方法 1 或方法 2 比較字符串沒有實際區別,只要它們都是字符串類型。 但是,如果一個為空,一個是另一種類型(如整數),或者一個表示具有不同引用的對象,那么,如最初的問題所示,您可能會遇到比較內容是否相等可能不會返回什么你期待。

建議的解決方案:

因為在比較事物時使用==與使用.Equals並不完全相同,所以您可以改用靜態 String.Equals方法。 這樣,如果兩側的類型不同,您仍然會比較內容,如果一個為空,您將避免異常。

   bool areEqual = String.Equals("Somestring", MyString);  

寫的多一點,但在我看來,使用起來更安全。

以下是從 Microsoft 復制的一些信息:

public static bool Equals (string a, string b);

參數

a字符串

要比較的第一個字符串,或null

b字符串

要比較的第二個字符串,或null

返回Boolean

如果a的值與b的值相同,則為true 否則為false 如果ab都為null ,則該方法返回true

作為對已經很好的答案的補充:此行為不僅限於字符串或比較不同的數字類型。 即使兩個元素都是相同基礎類型的 object 類型。 “==”不起作用。

以下屏幕截圖顯示了比較兩個對象 {int} - 值的結果

來自 VS2017 的示例

給答案補充一點。

.EqualsTo()方法為您提供了與文化和區分大小寫進行比較的條件。

@BlueMonkMN 的早期答案還有另一個維度。 額外的維度是,@Drahcir 標題問題的答案也取決於我們如何獲得string值。 為了顯示:

string s1 = "test";
string s2 = "test";
string s3 = "test1".Substring(0, 4);
object s4 = s3;
string s5 = "te" + "st";
object s6 = s5;
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s2), s1 == s2, s1.Equals(s2));

Console.WriteLine("\n  Case1 - A method changes the value:");
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s3), s1 == s3, s1.Equals(s3));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s4), s1 == s4, s1.Equals(s4));

Console.WriteLine("\n  Case2 - Having only literals allows to arrive at a literal:");
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s5), s1 == s5, s1.Equals(s5));
Console.WriteLine("{0} {1} {2}", object.ReferenceEquals(s1, s6), s1 == s6, s1.Equals(s6));

輸出是:

True True True

  Case1 - A method changes the value:
False True True
False False True

  Case2 - Having only literals allows to arrive at a literal:
True True True
True True True

我在這里有點困惑。 如果 Content 的運行時類型是字符串類型,則 == 和 Equals 都應返回 true。 但是,由於情況似乎並非如此,因此內容的運行時類型不是字符串,並且對其調用 Equals 是在執行引用相等,這解釋了 Equals("Energy Attack") 失敗的原因。 然而,在第二種情況下,關於應該調用哪個重載 == 靜態運算符的決定是在編譯時做出的,這個決定似乎是 ==(string,string)。 這向我表明 Content 提供了到字符串的隱式轉換。

真的很棒的答案和例子!

我只想補充兩者之間的根本區別,

==等運算符不是多態的,而Equals是多態的

考慮到這個概念,如果您計算出任何示例(通過查看左手和右手引用類型,並檢查/知道該類型是否實際具有 == 運算符重載和 Equals 被覆蓋),您一定會得到正確的答案.

這是由於值相等(相等方法)和引用相等(== 運算符),因為相等方法檢查值,而相同的 == 檢查引用。

== 運算符覆蓋代碼在https://referencesource.microsoft.com/上的字符串類中可用

所以現在更容易理解了,equal 方法也有 2 個實現,一個來自字符串類本身,一個來自對象類。 它對性能的影響以及我還運行了一些基本代碼並嘗試了解基准測試。

我在下面分享結果請糾正或建議我是否在某處錯了。 有 3 個案例,我為所有案例運行了相同的代碼,這就是結果。

案例 1:這里我使用的是字符串。 用於比較 2 個字符串且兩個字符串具有相同值的 equal 方法。 string.equals(a,b)

第一次運行:5608195 滴答

第二次運行:5529387 滴答

第三次運行:5622569 滴答聲

總滴答數:16760151

情況 2:這里我使用的是字符串。 用於比較 2 個字符串並且兩個字符串具有相同值的 equal() 方法(重載一個)。 a.等於(b)

第一次運行:6738583 個滴答聲

第二次運行:6452927 個滴答聲

第三次運行:7168897 個滴答聲

總滴答數=20360407

情況 3:這里我使用 == 運算符來比較 2 個字符串,並且兩個字符串都具有相同的值。 a==b

第一次運行:6652151 個滴答聲

第二次運行:7514300 滴答

第三次運行:7634606 滴答

總滴答數=21801057

class Program
{
    private static int count;
    static string a = "abcdef";
    static string b = "abcdef";
    static void Main(string[] args)
    {            

        for (int j = 1; j <= 3; j++)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            for (int i = 1; i <= 1000; i++)
            {
                checkString();
            }
            sw.Stop();
            Console.WriteLine(sw.ElapsedTicks);
        }
        Console.ReadLine();

    }
    public static void checkString()
    {
        for (int i = 1; i <= 100000; i++)
        {
            if (a==b)
                count++;
        }
    }
}

C# 中的==標記用於兩個不同的相等檢查運算符。 當編譯器遇到該標記時,它將檢查被比較的任一類型是否已為被比較的特定組合類型 (*) 或兩種類型都可以轉換為的類型組合實現了相等運算符重載。 如果編譯器發現這樣的重載,它將使用它。 否則,如果這兩種類型都是引用類型並且它們不是不相關的類(可能是接口,也可能是相關類),編譯器會將==視為引用比較運算符。 如果兩個條件都不適用,編譯將失敗。

請注意,其他一些語言對兩個相等檢查運算符使用單獨的標記。 例如,在 VB.NET 中, =標記在表達式中僅用於可重載的相等檢查運算符,而Is用作引用測試或空測試運算符。 在不覆蓋相等檢查運算符的類型上使用=將失敗,嘗試將Is用於測試引用相等性或無效性以外的任何目的也會失敗。

(*) 類型通常只重載相等以與自身進行比較,但對於類型來說,重載相等運算符以與其他特定類型進行比較可能很有用; 例如, int可以(恕我直言應該有但沒有)定義了一個相等的運算符來與float進行比較,這樣 16777217 就不會報告自己等於 16777216f。 實際上,由於沒有定義這樣的運算符,C# 會將int提升為float ,在相等檢查運算符看到它之前將其四舍五入為 16777216f ; 該運算符然后看到兩個相等的浮點數並將它們報告為相等,不知道發生的舍入。

Equals()==是否相同取決於實現 因為 C# 允許用戶分別為Equals()==設置不同的行為。

    class CompareTest
    {
        public readonly int val;

        public CompareTest(int val)
        {
            this.val = val;
        }

        public override bool Equals(object obj)
        {
            return obj is CompareTest test && this.val == test.val;
        }

        public override int GetHashCode()
        {
            return val;
        }

        public static bool operator == (CompareTest a, object b)
        {
            return Equals(a, b);
        }

        public static bool operator != (CompareTest a, object b)
        {
            return !(a == b);
        }
    }

在此示例中,我使Equals()==具有相同的行為。 但是如果我讓它們不同呢? 例如:

        public static bool operator == (CompareTest a, object b)
        {
            return false;
        }

Equals()正常工作,但==永遠不會工作。

此外,雖然我使它們具有相同的行為,但仍有一個區別:將調用哪個== function 取決於左側的值:

        Compare Test a = new CompareTest(1);
        object b = new CompareTest(1);
        CompareTest c = new CompareTest(1);
        Debug.Log("AB " + (a == b)); // true
        Debug.Log("BA " + (b == a)); // false! because it calls object's == function 
        Debug.Log("AC " + (a == c)); // true
        Debug.Log("CA " + (c == a)); // true

當我們創建任何對象時,對象有兩個部分,一個是內容,另一個是對該內容的引用。 ==比較內容和參考; equals()只比較內容

http://www.codeproject.com/Articles/584128/What-is-the-difference-between-equalsequals-and-Eq

請注意,C# 中有兩種不同類型的相等性

1- Value Equality (對於 int、DateTime 和 struct 等值類型)

2- Reference Equality (對於對象)

有兩種基本的標准協議用於實現相等性檢查。

1- ==!=運算符。

2- virtual Equals方法。

== 和 != 是靜態解析的,這意味着 C# 將在編譯時決定哪種類型將執行比較。

例如value-type

 int x = 50;
 int y = 50;
 Console.WriteLine (x == y); // True

但對於reference type

 object x = 50;
 object y = 50;
 Console.WriteLine (x == y); // False 

Equals()最初在運行時根據操作數的實際類型進行解析。

例如,在以下示例中,在運行時,將決定Equals()將應用於 int 值,結果為true

object x = 5;
object y = 5;
Console.WriteLine (x.Equals (y)); // True

但是,對於引用類型,它將使用引用相等性檢查。

MyObject x = new MyObject();
MyObject y = x;
Console.WriteLine (x.Equals (y)); // True

請注意, Equals()struct使用結構比較,這意味着它在結構的每個字段上調用 ​​Equals。

==

== 運算符可用於比較任何類型的兩個變量,它只是比較位

int a = 3;
byte b = 3;
if (a == b) { // true }

注意:int 的左側有更多的零,但我們在這里不關心。

int a (00000011) == 字節 b (00000011)

記住 == 運算符只關心變量中位的模式。

使用 == 如果兩個引用(原語)指向堆上的同一個對象。

無論變量是引用還是原始變量,規則都是相同的。

Foo a = new Foo();
Foo b = new Foo();
Foo c = a;

if (a == b) { // false }
if (a == c) { // true }
if (b == c) { // false }

a == c 為真 a == b 為假

a 和 c 的位模式相同,因此使用 == 時它們相等。

平等的():

使用 equals() 方法查看兩個不同的對象是否相等

例如兩個不同的 String 對象,它們都代表“Jane”中的字符

Equal 和 == 之間的唯一區別在於對象類型比較。 在其他情況下,例如引用類型和值類型,它們幾乎相同(要么都是按位相等,要么都是引用相等)。

object: Equals: 按位相等 ==: 引用相等

字符串:(字符串的equals和==是一樣的,但是如果字符串中的一個變成了對象,那么比較結果就會不同) Equals: bit-wisequality == : bit-wisequality

有關更多解釋,請參見此處

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM