亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

C#中的==運算符用法講解

 更新時間:2022年02月21日 08:33:12   作者:Sweet-Tang  
本文詳細講解了C#中的==運算符的用法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下

==運算符與基元類型

我們分別用兩種方式比較兩個整數(shù),第一個使用的是Equals(int)方法,每二個使用的是==運算符:

class Program
{
    static void Main(String[] args)
    {
        int num1 = 5;
        int num2 = 5;

        Console.WriteLine(num1.Equals(num2));
        Console.WriteLine(num1 == num2);
    }
}

運行上面的示例,兩個語句出的結(jié)果均為true。我們通過ildasm.exe工具進行反編譯,查看IL代碼,了解底層是如何執(zhí)行的。

如果您以前從來沒有接觸過IL指令,不過沒關(guān)系,在這里您不需要理解所有的指令,我們只是想了解這兩個比較方式的差異。

您可以看到這樣一行代碼:

IL_0008:  call       instance bool [mscorlib]System.Int32::Equals(int32)

在這里調(diào)用的是int類型Equals(Int32)方法(該方法是IEquatable<Int>接口的實現(xiàn))。

現(xiàn)在再來看看使用==運算符比較生成的IL指令:

IL_0015:  ceq

您可以看到,==運行符使用的是ceq指令,它是使用CPU寄存器來比較兩個值。C#==運算符底層機制是使用ceq指令對基元類型進行比較,而不是調(diào)用Equals方法。

==運算符與引用類型

修改上面的示例代碼,將int類型改為引用類型,編譯后通過ildasm.exe工具反編譯查看IL代碼。

class Program
{
    static void Main(String[] args)
    {
        Person p1 = new Person();
        p1.Name = "Person1";

        Person p2 = new Person();
        p2.Name = "Person1";

        Console.WriteLine(p1.Equals(p2));
        Console.WriteLine(p1 == p2);
    }
}

上述C#代碼的IL代碼如下所示:

我們看到p1.Equals(p2)代碼,它是通過調(diào)用Object.Equals(Object)虛方法來比較相等,這是在意料之中的事情;現(xiàn)在我們來看==運算符生成的IL代碼,與基元類型一致,使用的也是ceq指令。

==運算符與String類型

接來下來看String類型的例子:

class Program
{
    static void Main(String[] args)
    {
        string s1 = "Sweet";
        string s2 = String.Copy(s1);

        Console.WriteLine(ReferenceEquals(s1, s2));
        Console.WriteLine(s1 == s2);
        Console.WriteLine(s1.Equals(s2));
    }
}

上面的代碼與我們以前看過的非常相似,但是這次我們使用String類型的變量。我們建一個字符串,并付給s1變量,在下一行代碼我們創(chuàng)建這個字符串的副本,并付給另一個變量名稱s2。

運行上面的代碼,在控制臺輸出的結(jié)果如下:

您可以看到ReferenceEquals返回false,這意味著這兩個變量是不同的實例,但是==運算符和Equals方法返回的均是true。在String類型中,==運算符執(zhí)行的結(jié)果與Equals執(zhí)行的結(jié)果一樣。

同樣我們使用過ildasm.exe工具反編譯查看生成IL代碼。

在這里我們沒有看到ceq指令,對String類型使用==運算符判斷相等時,調(diào)用的是一個op_equality(string,string)的新方法,該方法需要兩個String類型的參數(shù),那么它到底是什么呢?

答案是String類型提供了==運算符的重載。在C#中,當我們定義一個類型時,我們可以重載該類型的==運算符;例如,對于以前的例子中我們實現(xiàn)的Person類,如果我們?yōu)樗剌d==運算符,大致的代碼如下:

public class Person
{

    public string Name { get; set; }

    public static bool operator ==(Person p1, Person p2)
    {
        // 注意這里不能使用==,否則會導(dǎo)致StackOverflowException
        if (ReferenceEquals(p1, p2))
            return true;

        if (ReferenceEquals(p1, null) || ReferenceEquals(p2, null))
            return false;

          return p1.Name == p2.Name;
    }

    public static bool operator !=(Person p1, Person p2)
    {
        return !(p1 == p2);
    }
}

上面的代碼很簡單,我們實現(xiàn)了==運算符重載,這是一個靜態(tài)方法,但這里要注意的是,方法的名稱是perator==,與靜態(tài)方法的相似性;事實上,它們會被由編譯器成一個名稱為op_Equality()的特殊靜態(tài)方法。

為了使用事情更加清楚,我們查看微軟實現(xiàn)的String類型。

在上面的截圖中,我們可以看到,有兩個運算符的重載,一個用于相等,另一個是不等式運算符,其運算方式完全相同,但是否定等于運算符輸出。需要注意的一點是,如果您想重載一個類型的==運行符的實現(xiàn),那么您還需要重載!=操作符的實現(xiàn),否則編譯會報錯。

==運算符與值類型

在演示值類型的示例前,我們先將Person類型從引用類型改為值類型,Person定義如下:

public struct Person
{
    public string Name { get; set; }

    public Person(string name)
    {
        Name = name;
    }

    public override string ToString()
    {

        return Name;
    }
}

我們將示例代碼改為如下:

class Program
 {
     static void Main(String[] args)
     {
         Person p1 = new Person("Person1");
         Person p2 = new Person("Person2");

         Console.WriteLine(p1.Equals(p2));
         Console.WriteLine(p1 == p2);
     }
 }

當我們在嘗試編譯上述代碼時,VS將提示如下錯誤:

根據(jù)錯誤提示,我們需要實現(xiàn)Person結(jié)構(gòu)體的==運算符重載,重載的語句如下(忽略具體的邏輯):

public static bool operator ==(Person p1, Person p2)
 {
 }
 public static bool operator !=(Person p1, Person p2)
 {
 }

當添加上面代碼后,重新編譯程序,通過ildasm.exe工具反編譯查看IL代碼,發(fā)現(xiàn)值類型==運算符調(diào)用也是op_Equality方法。

關(guān)于值類型,我們還需要說明一個問題,在不重寫Equals(object)方法時,該方法實現(xiàn)的原理是通過反射遍歷所有字段并檢查每個字段的相等性,關(guān)于這一點,我們不演示;對于值類型,最好重寫該方法。

==運算符與泛型

我們編寫另一段示例代碼,聲明兩個String類型變量,通過4種不同的方式比較運算:

public class Program
{
    public static void Main(string[] args)
    {
        string str = "Sweet";
        string str1 = string.Copy(str);

        Console.WriteLine(ReferenceEquals(str, str1));
        Console.WriteLine(str.Equals(str1));
        Console.WriteLine(str == str1);
        Console.WriteLine(object.Equals(str, str1));
    }
}

輸出的結(jié)果如下:

首先,我們使用ReferenceEquals方法判斷兩個String變量都引用相同,接下來我們再使用實例方法Equals(string),在第三行,我們使用==運算符,最后,我們使用靜態(tài)方法Object.quals(object,object)(該方法最終調(diào)用的是String類型重寫的Object.Equals(object)方法)。我們得到結(jié)論是:

ReferenceEquals方法返回false,因為它們不是同一個對象的引用;String類型的Equals(string)方法返回也是true,因為兩個String類型是相同的(即相同的序列或字符);==運算符也將返回true,因為這兩個String類型的值相同的;虛方法Object.Equals也將返回true,這是因為在String類型重寫了方法,判斷的是String是否值相同。

現(xiàn)在我們來修改一下這個代碼,將String類型改為Object類型:

public class Program
{
    public static void Main(string[] args)
    {
        object str = "Sweet";
        object str1 = string.Copy((string)str);

        Console.WriteLine(ReferenceEquals(str, str1));
        Console.WriteLine(str.Equals(str1));
        Console.WriteLine(str == str1);
        Console.WriteLine(object.Equals(str, str1));
    }
}

運行的結(jié)果如下:

第三種方法返回的結(jié)果與修改之前不一致,==運算符返回的結(jié)果是false,這是為什么呢?

這是因為==運算符實際上是一個靜態(tài)的方法,對一非虛方法,在編譯時就已經(jīng)決定用調(diào)用的是哪一個方法。在上面的例子中,引用類型使用的是ceq指令,而String類型調(diào)用是靜態(tài)的op_Equality方法;這兩個實例不是同一個對象的引用,所以ceq指令執(zhí)行后的結(jié)果是false。

再來說一下==運算符與泛型的問題,我們創(chuàng)建一個簡單的方法,通過泛型方法判斷兩個泛型參數(shù)是否相等并在控制臺上打印出結(jié)果:

static void Equals<T>(T a, T b)
{
    Console.WriteLine(a == b);
}

但是當我們編譯這段代碼時,VS提示如下錯誤:

上面顯示的錯誤很簡單,不能使用==運算符比較兩個泛型T。因為T可以是任何類型,它可以是引用類型、值類型,不能提供==運算符的具體實現(xiàn)。

如果像下面這樣修改一下代碼:

static void Equals<T>(T a, T b) where T : class
{
    Console.WriteLine(a == b);
}

當我們將泛型類型T改為引用類型,能成功編譯;修改Main方法中的代碼,創(chuàng)建兩個相同的String類型,和以前的例子一樣:

public class Program
{
    static void Main(string[] args)
    {
        string str = "Sweet";
        string str1 = string.Copy(str);

        Equals(str, str1);
    }

    static void Equals<T>(T a, T b) where T : class
    {
        Console.WriteLine(a == b);
    }
}

輸出的結(jié)果如下:

結(jié)果與您預(yù)期的結(jié)果不一樣吧,我們期待的結(jié)果是true,輸出的結(jié)果是false。不過仔細思考一下,也許會找到答案,因為泛型的約束是引用類型,==運算符對于引用類型使用的是引用相等,IL代碼可以證明這一點:

如果我們泛型方法中的==運算符改為使用Equals方法,代碼如下:

static void Equals<T>(T a, T b)
{
    Console.WriteLine(object.Equals(a, b));
}

我們改用Equals,也可以去掉class約束;如果我們再次運行代碼,控制臺打印的結(jié)果與我們預(yù)期的一致,這是因為調(diào)用是虛方法object.Equals(object)重寫之后的實現(xiàn)。

但是其它的問題來了,如果對于值類型,這里就會產(chǎn)生裝箱,有沒有解決的辦法呢?關(guān)于這一點,我們直接給出答案,有時間專門來討論這個問題。

將比較的值類型實現(xiàn)IEquatable<T>接口,并將比較的代碼改為如下,這樣可以避免裝箱(關(guān)于這一點,可以參考博客:http://chabaoo.cn/article/238142.htm):

static void Equals<T>(T a, T b)
{
    Console.WriteLine(EqualityComparer<T>.Default.Equals(a, b));
}

總結(jié)

對于基元類型==運算符的底層機制使用的是ceq指令,通過CPU寄存器進行比較;

對于引用類型==運算符,它也使用的ceq指令來比較內(nèi)存地址;

對于重載==運算符的類型,實際上調(diào)用的是op_equality這個特殊的方法;

盡量保證==操作符重載和Object.Equals(Object)虛方法的寫返回的是相同的結(jié)果;

對于值類型,Equals方法默認是通過反射遍歷所有字段并檢查每個字段的相等性,為了提高性能,我們需要重寫該方法;

值類型默認情況下不能使用==運算符,需要實現(xiàn)==運算符的重載;

由于==運算符重載實現(xiàn)實際上是一個靜態(tài)的方法,在泛型類或方法中使用時與實際的結(jié)果可能存在差別,使用Equals方法可以避免這個問題。

到此這篇關(guān)于C#中的==運算符用法講解的文章就介紹到這了。希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • .net(c#)中的new關(guān)鍵字詳細介紹

    .net(c#)中的new關(guān)鍵字詳細介紹

    在 C# 中,new 關(guān)鍵字可用作運算符、修飾符或約束
    2013-10-10
  • 基于C#實現(xiàn)FTP下載文件

    基于C#實現(xiàn)FTP下載文件

    這篇文章主要為大家詳細介紹了如何利用C#實現(xiàn)FTP下載文件,文中的示例代碼講解詳細,對我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-12-12
  • C#設(shè)置自定義文件圖標實現(xiàn)雙擊啟動(修改注冊表)

    C#設(shè)置自定義文件圖標實現(xiàn)雙擊啟動(修改注冊表)

    這篇文章介紹的是利用C#設(shè)置自定義文件圖標,然后實現(xiàn)雙擊啟動的功能,文章給出了示例代碼,介紹的很詳細,有需要的可以參考借鑒。
    2016-08-08
  • C#下解析HTML的兩種方法介紹

    C#下解析HTML的兩種方法介紹

    用System.Net.WebClient下載Web Page存到本地文件或者String中,用正則表達式來分析。這個方法可以用在Web Crawler等需要分析很多Web Page的應(yīng)用中
    2013-09-09
  • C#實現(xiàn)的微信網(wǎng)頁授權(quán)操作邏輯封裝示例

    C#實現(xiàn)的微信網(wǎng)頁授權(quán)操作邏輯封裝示例

    這篇文章主要介紹了C#實現(xiàn)的微信網(wǎng)頁授權(quán)操作邏輯封裝,分析了微信網(wǎng)頁授權(quán)操作的原理、步驟并給出了C#實現(xiàn)的網(wǎng)頁授權(quán)操作邏輯封裝類,需要的朋友可以參考下
    2016-10-10
  • Asp.Net中MVC緩存詳解

    Asp.Net中MVC緩存詳解

    這篇文章主要介紹了Asp.Net中MVC緩存的種類區(qū)別等內(nèi)容,一下來學(xué)習(xí)下。
    2017-12-12
  • 基于C#實現(xiàn)電腦系統(tǒng)掛機鎖

    基于C#實現(xiàn)電腦系統(tǒng)掛機鎖

    這篇文章主要為大家詳細介紹了如何利用C#實現(xiàn)電腦系統(tǒng)掛機鎖,文中的示例代碼講解詳細,對我們學(xué)習(xí)C#有一定的幫助,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-12-12
  • 在WPF中實現(xiàn)全局快捷鍵功能

    在WPF中實現(xiàn)全局快捷鍵功能

    這篇文章介紹了在WPF中實現(xiàn)全局快捷鍵功能的方法,文中通過示例代碼介紹的非常詳細。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-06-06
  • C#實現(xiàn)計算器窗體程序

    C#實現(xiàn)計算器窗體程序

    這篇文章主要為大家詳細介紹了C#實現(xiàn)計算器窗體程序,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01
  • C# SQLite執(zhí)行效率的優(yōu)化教程

    C# SQLite執(zhí)行效率的優(yōu)化教程

    這篇文章主要給大家介紹了關(guān)于C# SQLite執(zhí)行效率優(yōu)化的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用C# SQLite具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-06-06

最新評論