C#基礎之Equals和Dispose

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

  由于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都分配了內存并str1和str2指向各自的內存,但是結果卻是true。我感覺很有可能是因為string類的“特殊”,才發現自己一直都沒有理解string類的特殊。百度之后發現原來是.NET做的優化,由于str1和str2內容相同,為了節省內存故讓str1和str2指向同一個內存區域,這樣就不用為str2開辟空間了。這種技術是字符串駐留技術,當CLR初始化時,會創建一個內部的散列表,鍵為字符串,值Wie指向堆中字符串的引用,JIT編譯方法時,添加str1和“test”到空的散列表里了,str2賦值時會先看散列表里有沒有相等的,有的就直接指向相等的值的引用。編程中當把string作為參數傳遞時不會改變原有string對象的值,因為每次賦值會另外開辟內存,這就是string引用類型的特殊之處。

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

 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);                   //TrueConsole.WriteLine(obj1.Equals(obj2));           //True//這種情況的賦值內存沒有做優化,故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);                   //FalseConsole.WriteLine(obj3.Equals(obj4));            //True//當用new開辟新的空間創建對象時,people1和people2肯定是指向了不同的內存地址//而且這是2個不同的對象,我們不能只看它們有相同的方法相同的字段,還要看它們的標識等環境變量,//對于people3和peop4來說則是指向了同一塊內存區域People people1 = new People("小方");People people2 = new People("小方");Console.WriteLine(people1 == people2);                   //FalseConsole.WriteLine(people1.Equals(people2));            //FalsePeople people3 = new People("小白");People people4 = people3;Console.WriteLine(people3 == people4);                   //TrueConsole.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個變量指向的堆中的內容是否相等。

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

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

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

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

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

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

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

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

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

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

?聲明:本文原創發表于博客園,作者為方小白,如有錯誤歡迎指出?。本文未經作者許可不許轉載,否則視為侵權。

轉載于:https://www.cnblogs.com/fangyz/p/5293888.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/272157.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/272157.shtml
英文地址,請注明出處:http://en.pswp.cn/news/272157.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

mysql 時間chuo格式化_Mysql時間戳與時間格式轉換問題匯總

一、時間戳的定義時間戳指格林威治時間1970年01月01日00時00分00秒起至現在的總秒數。二、時間格式轉時間戳的方法使用unix_timestamp函數,如下:SELECT UNIX_TIMESTAMP();//返回當前時間戳SELECT UNIX_TIMESTAMP(2017-12-16 17:29:56) AS t; //返回指定時…

文件系統(文件系統目錄結構、磁盤分區、虛擬文件系統)、linux內核結構框圖

什么是文件系統? 常規認知就是根目錄下那些文件,但其實并不是那樣。文件系統是操作系統用于明確存儲設備(常見的是磁盤,也有基于NAND Flash的固態硬盤)或分區上的文件的方法和數據結構;即在存儲設備上組織…

dockerq啟動報錯(iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 9876 -j DNAT --t

docker啟動報錯 (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 9876 -j DNAT --to-destination 172.17.0.2:9876 ! -i docker0: iptables: No chain/target/match by that name. 解決方案:重啟docker systemctl restart docker

Linux進程終止命令kill或kill all?筆記

在linux命令下,如果需要終止某個進程,可以使用kill或者killall等命令來實現。終止命令的原理都是向linux內核發送一個系統操作的信號以及某個進程的ID,然后系統內核會根據指定的進程ID進行相應的處理。 kill命令典型的用法:首先使…

linux驅動(驅動編譯、字符設備驅動框架、交叉編譯樹莓派驅動、樹莓派驅動本地編譯)

什么是驅動: 驅動就是對底層硬件設備的操作進行封裝,并向上層提供函數接口。 設備分類: linux系統將設備分為3類:字符設備、塊設備、網絡設備。 字符設備:指只能一個字節一個字節讀寫的設備,不能隨機讀取…

docker啟動報錯 ?(iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 9876 -j DNAT --

docker啟動報錯 : (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 9876 -j DNAT --to-destination 172.17.0.2:9876 ! -i docker0: iptables: No chain/target/match by that name. 解決方案: systemctl restart docker

第一個Spark程序

1、Java下Spark開發環境搭建(from http://www.cnblogs.com/eczhou/p/5216918.html)1.1、jdk安裝安裝oracle下的jdk,我安裝的是jdk 1.7,安裝完新建系統環境變量JAVA_HOME,變量值為“C:\Program Files\Java\jdk1.7.0_79”…

arduino判斷是否連接串口_Arduino-串口通信

Serial”系列函數,所以我們要對其有所了解,下面介紹幾個常“Serial”函數。1、Serial.begin()—設置串行每秒傳輸數據的速率(波特率)。在同計算機通訊時,使用下面這些值:300, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400…

最詳細的docker安裝rocketMQ教程來了

RocketMQ是一款分布式、隊列模型的消息中間件,是由阿里巴巴設計的,具有以下特點: 支持嚴格的消息順序 支持Topic與Queue兩種模式 億級消息堆積能力 比較友好的分布式特性 同時支持Push與Pull方式消費消息 歷經多次天貓雙十一海量消息考驗…

樹莓派IO口驅動代碼的編寫、微機總線地址、物理地址、虛擬地址、BCM2835芯片手冊

地址總線: 百度百科解釋: 地址總線 (Address Bus;又稱:位址總線) 屬于一種電腦總線 (一部份),是由CPU 或有DMA 能力的單元,用來溝通這些單元想要存取(讀取/寫入&#xff…

奪命雷公狗---DEDECMS----26dedecms面包屑導航的實現

我們在很多項目里面都會用到面包屑導航,而dedecms里面也是給我們封裝好面包屑導航的了,如下圖所示: 在dede里面實現面包屑導航主要用到{dede:field.position/}標簽,我們首先來修改下article_movie.htm內容頁的模版文件: 我們修改成…

rust油桶用什么打_草莓用什么膨大素好?草莓膨大劑什么時間打?草莓用什么肥料膨大...

農資365公眾號,了解更多生根、根腐、重茬、土傳、枯黃萎、根爛病、防治根結線蟲、微生物菌肥、膨大坐果、抗病增產的防治方法!草莓含有豐富的營養,并且種植效益較高,其種植范圍也比較廣。草莓種植期間有很多因素影響草莓果實膨大&…

docker安裝kafka,超級簡單的

簡介 kafka是一個分布式消息隊列。具有高性能、持久化、多副本備份、橫向擴展能力。生產者往隊列里寫消息,消費者從隊列里取消息進行業務邏輯。一般在架構設計中起到解耦、削峰、異步處理的作用。 kafka對外使用topic的概念,生產者往topic里寫消息&…

Linux中常見的環境變量筆記

1、變量:BASHBash Shell的全路徑比如:echo $BASH2、變量:BASH_VERSIONBash Shell的版本號3、變量:EUID記錄當前用戶的UID。root用戶值為0。4、FUNCNAME在用戶函數體內部,記錄當前函數體的函數名。5、變量:H…

消防信號二總線有沒電壓_春曉161#地塊人防工程消防電源監控系統的設計與應用...

涂志燕安科瑞電氣股份有限公司,上海 嘉定 201801;摘要:本文簡述了消防設備電源的組成原理,分析了消防設備電源監控系統在應用中的設計依據和相關規范。通過安科瑞消防設備電源監控系統在春曉161#地塊項目的實例介紹,闡…

大學慕課數據結構單元測試——華中科技大學

第一章緒論單元測試 一、單選(2分) 1、?___C__ 是數據的最小單位。 A.信息項 B.數據元素 C.數據項 D.表元素 2、?以下說法不正確的是 ___B___。 A.數據元素是數據的基本單位 B.數據項可由若干個數據元素構成 C.數據可由若干個數據元素構成 D.數據項是不可分割的最小…

gitlab應用

1.git config --global user.email "mybimt.com"  //注冊本地環境 2.ssh-keygen -t rsa -C "mybimt.com" //生成本機的key 3.在gitlab加入.ssh中生成的key //gitlab中注冊本機 4.git clone gitmy.git …

RocketMQ同步刷盤和異步刷盤

刷盤機制 同步刷盤和異步刷盤 在broker配置文件里修改參數配置是同步還是異步

vim模式下報錯E37: No write since last change No write since last change for buffer “ “

報錯如下圖所示: 網上的解決方法: 文件為只讀文件,無法修改。使用命令:w!強制存盤即可在vim模式下,鍵入以下命令::w!存盤后在使用vim命令檢查是否保存,如未保存,編輯后重復以上操作…

Linux中Shell中取消變量和特殊變量的筆記

1、取消變量取消變量也就是將變量從內存中釋放出去,可以使用unset 后面加變量名即可,當然函數的釋放同樣可以采用該方式處理。比如:name"123"echo ${name}輸出:123unset nameecho ${name}輸出:#取消函數示例…