<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          看是簡單并不簡單:C#基礎之Equals和Dispose

          共 6846字,需瀏覽 14分鐘

           ·

          2024-04-10 17:54

          1.equal()和運算符==的區(qū)別

            由于C#中有值類型和引用類型,那么相等也分為值相等和引用相等。先來看一個值類型簡單的例子,順便也寫了string類型的比較。

                
                  
                    
               static void Main(string[] args) { int n1 = 1; int n2 = 1; Console.WriteLine(n1==n2); Console.WriteLine(n1.Equals(n2)); string str1 = "test"; string str2 = "test"; Console.WriteLine(str1==str2); Console.WriteLine(str1.Equals(str2)); Console.ReadKey(); //結果是4個 True }

          前2個結果為true我可以理解,但是后2個為true我有點懷疑。一直記得不斷有人說string是特殊的引用類型,那這里應該是為str1和str2都分配了內(nèi)存并str1和str2指向各自的內(nèi)存,但是結果卻是true。我感覺很有可能是因為string類的“特殊”,才發(fā)現(xiàn)自己一直都沒有理解string類的特殊。百度之后發(fā)現(xiàn)原來是.NET做的優(yōu)化,由于str1和str2內(nèi)容相同,為了節(jié)省內(nèi)存故讓str1和str2指向同一個內(nèi)存區(qū)域,這樣就不用為str2開辟空間了。這種技術是字符串駐留技術,當CLR初始化時,會創(chuàng)建一個內(nèi)部的散列表,鍵為字符串,值Wie指向堆中字符串的引用,JIT編譯方法時,添加str1和“test”到空的散列表里了,str2賦值時會先看散列表里有沒有相等的,有的就直接指向相等的值的引用。編程中當把string作為參數(shù)傳遞時不會改變原有string對象的值,因為每次賦值會另外開辟內(nèi)存,這就是string引用類型的特殊之處。

            對于引用類型,==比較的是兩個引用是不是指向同一個內(nèi)存地址,equal()方法比較的是兩個對象指向的內(nèi)存空間內(nèi)容是否相同。下面是關于引用類型的代碼。

                
                  
                    
          class Program { static void Main(string[] args) { //前面已提過這里str1和str2指向同一個引用,故obj1和obj2表示的地址是相同的 string str1 = "test"; string str2 = "test"; object obj1 = str1; object obj2 = str2; Console.WriteLine(obj1 == obj2); //True Console.WriteLine(obj1.Equals(obj2));   //True //這種情況的賦值內(nèi)存沒有做優(yōu)化,故obj3和obj4表示的是不一樣的地址 string str3 = new string(new char[] { 't', 'e', 's', 't' }); string str4 = new string(new char[] { 't', 'e', 's', 't' }); object obj3 = str3; object obj4 = str4; Console.WriteLine(obj3 == obj4); //False Console.WriteLine(obj3.Equals(obj4));   //True //當用new開辟新的空間創(chuàng)建對象時,people1和people2肯定是指向了不同的內(nèi)存地址 //而且這是2個不同的對象,我們不能只看它們有相同的方法相同的字段,還要看它們的標識等環(huán)境變量, //對于people3和peop4來說則是指向了同一塊內(nèi)存區(qū)域 People people1 = new People("小方"); People people2 = new People("小方"); Console.WriteLine(people1 == people2); //False Console.WriteLine(people1.Equals(people2));   //False People people3 = new People("小白"); People people4 = people3; Console.WriteLine(people3 == people4); //True Console.WriteLine(people3.Equals(people4));   //True Console.ReadKey(); } } class People { string name = null; public string Name { get { return name; } set { name = value; } } public People(string strName) { name = strName; } }

          引用類型的變量是存在棧中,但指向的是堆中的地址。==操作符比較的是2個變量的值是否相等,那對于引用類型也就是比較它們表示的地址是否相同。equal表示的是2個變量指向的堆中的內(nèi)容是否相等。


          2.思考:為什么要封裝字段

            在上面的例子中,已經(jīng)形成習慣將字段封裝成屬性。我發(fā)現(xiàn)學習過程中大家總是這么寫,我有時候會想一下為什么要這樣寫,但可能還沒有做過實際的項目開發(fā)(沒有因為不這樣寫被坑過),所有我一直沒有想到最本質(zhì)的原因。因為經(jīng)常說為了保護數(shù)據(jù)的安全性不讓直接讀和寫,可是常常看到屬性中是既有set又有get的,那這樣還不是直接讀和寫,這樣有什么區(qū)別呢?今天既然又發(fā)現(xiàn)這個問題,不能還不解決了。查閱了前輩的經(jīng)驗后,我發(fā)現(xiàn)有2點說到本質(zhì)了:

          1:安全性,也就是我可以在屬性中進行判斷,比如age,我可以在set的時候加一個if判斷范圍在0到150,雖然也可以在外部判斷,但這樣寫是一定不會出錯的!

          2:當出現(xiàn)修改時,我們可以在屬性中進行修改,而不必修改整個程序。舉個例子,比如業(yè)務需求改變?yōu)槲覀兘o一個變量賦值,get時不是輸出原來的值,而是輸出這個值乘以2,如果該字段是public,整個程序用到該字段的地方都要修改,然而如果有屬性那就很好辦了,只需在get時乘以2就可以了。


          3.Dispose()、Close()、Finalize()的區(qū)別

            Close()和Dispose()差不多,只是因為Close這個詞更加容易理解,所以在Close()方法內(nèi)部調(diào)用了Dispose()方法。Dispose方法在內(nèi)部是去調(diào)用一個virtual的Dispose(bool)函數(shù)去釋放資源。具有Dispose()方法的類是實現(xiàn)了IDisposable接口的,很多類實現(xiàn)了IDisposable接口,但是只提供Close(),而不對外提供Dispose()。原因是這些類是顯示實現(xiàn)接口,這樣的話實現(xiàn)類對象將無法調(diào)用Dispose(),比如ClassA實現(xiàn)接口IDisposable接口,如果要調(diào)用Dispose方法則只能調(diào)用((IDisposable)new ClassA()).Dispose()。這樣做的目的也就是提供易于理解的Close()。例外有時候調(diào)用Close后我們還可以復活對象,而Dispose一旦被調(diào)用就會實實在在的釋放資源。

            其實.NET最基本的釋放資源方法是Finalize和Dispose這2個方法,F(xiàn)inalize是用于釋放非托管資源的,Dispose可以釋放所有資源,即可釋放托管資源,又可以釋放非托管資源。我們程序員是無法顯示調(diào)用Finalize方法的,這個方法當我們使用析構函數(shù)時才能被調(diào)用,但是程序員并不會知道什么時候會調(diào)用析構函數(shù),這將由垃圾回收器控制。只有當垃圾回收器認為對象符合析構的時候才會調(diào)用,程序退出時也會調(diào)用析構函數(shù)。為了提升性能,我們最好不要使用空的析構函數(shù),因為如果類包括析構函數(shù),則Finalize隊列中則會創(chuàng)建一個成員選項,GC處理這個隊列時如果選項內(nèi)容為空那只會導致不必要的性能。

            寫到這里我心里有2個疑問,上面提到建議我們不要使用空的析構函數(shù),可以理解是因為要提升性能,可是我寫析構函數(shù)不就是為了調(diào)用Finalize方法釋放資源嗎?還有既然Dispose方法可以釋放所有資源,何必還要寫一個Finalize方法呢?直到敲了前輩們寫的代碼才有了答案。

                
                  
                    
          public class People : IDisposable { //前面我們說了析構函數(shù)實際上是重寫了 System.Object 中的虛方法 Finalize, 默認情況下,一個類是沒有析構函數(shù)的,也就是說,對象被垃圾回收時不會被調(diào)用Finalize方法 ~People() { // 必須以Dispose(false)方式調(diào)用,以false告訴Dispose(bool disposing)函數(shù)是從垃圾回收器在調(diào)用Finalize時調(diào)用的 Dispose(false); } // 無法被客戶直接調(diào)用 // 如果 disposing 是 true, 那么這個方法是被客戶直接調(diào)用的,那么托管的,和非托管的資源都可以釋放 // 如果 disposing 是 false, 那么函數(shù)是從垃圾回收器在調(diào)用Finalize時調(diào)用的,此時會釋放托管資源 protected virtual void Dispose(bool disposing) { // 那么這個方法是被客戶直接調(diào)用的,那么托管的,和非托管的資源都可以釋放 if (disposing) { // 釋放 托管資源 //...... } //釋放非托管資源 //...... // 那么這個方法是被客戶直接調(diào)用的,告訴垃圾回收器從Finalization隊列中清除自己,從而阻止垃圾回收器調(diào)用Finalize方法. if (disposing) GC.SuppressFinalize(this); } //可以被客戶直接調(diào)用 public void Dispose() { Dispose(true); } }

          從上面的例子中可以看出Finalize和Dispose釋放了什么資源,而且如果是Dispose方式釋放資源后,還會調(diào)用GC.SuppressFinalize(this),這個方法會告訴GC沒必要再調(diào)用析構函數(shù)了,因為已經(jīng)使用Dispose方法釋放資源了。這說明析構函數(shù)只是作為資源釋放的一種補救措施,同樣的我們可以在析構函數(shù)中寫釋放非托管資源的代碼,最后再調(diào)用Finalize方法以保證資源被完全釋放,這樣就萬無一失了!

          筆者Dispose還沒有充分理解。


          4de286cd1ee7585fb86545134ffe081c.webp

              

          關注公眾號DotNet開發(fā)跳槽?    

          瀏覽 14
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  av中文天堂 | 微信群拉人 | 一本色道久久HEZYO亚洲精品 | 一本色道久久综合无码人妻 | 欧洲亚洲青纯在线无码 |