工作中能用到的基礎知識總結(一)


簡介

       該博文記錄一些工作中接觸較多的基礎知識點的總結。

系列目錄

  1. 工作中能用到的基礎知識總結(一)
  2. 工作中能用到的基礎知識總結(二)

知識點

一、值類型和引用類型

      定義:值類型派生自System.ValueType,包括一般的非字符串基本類型(decemal,int,float)、結構體、枚舉,可空類型(T?)。引用類型包括接口,類,數組,委托,string等需要人工new創建的類型。

      內存分配上有什么區別?

      值類型分配在它聲明的地方:作為字段時,跟隨其所屬的變量(實例)存儲。作為局部變量時(eg:方法內部局部變量),存儲在棧上;而引用類型的的聲明(指針)放在棧里面,而對應的實例對象(new 的對象)放在堆里面,引用類型在 C++中需要人工回收,而在C#中GC的3級回收機制可以自動回收托管代碼。

      錯誤回答:值類型放在內存棧里面,而引用類型放在堆里面。

      分配內存為什么不同?

      值類型類型簡單,而引用類型相對復雜,引用類型會引用值類型或與其他引用類型有相互引用關系。簡單來說,對象類型需要動態內存而基本類型則需要靜態內存。若需要分配動態內存(GC操作動態內存),那么就分配到堆上;反之在棧上。對於值類型,它們的內存值都分配在棧上,當我們把一個值類型分配給另外一個值類型時,需要創建一個完全不同的拷貝;對於引用類型,當我們創建一個對象,並把一個對象賦給另外一個對象時,它們的指針指向相同的內存。改變其中一個,會影響到另外一個。

 

二、裝箱/拆箱問題

      裝箱:用於在垃圾回收堆中存儲值類型。裝箱是值類型到 object 類型或到此值類型所實現的任何接口類型的隱式轉換。 
      拆箱:從 object 類型到值類型或從接口類型到實現該接口的值類型的顯式轉換。

      為何需要裝箱?       

      一種最普通的場景是,調用一個含類型為Object的參數的方法,該Object可支持任意為型,以便通用。當你需要將一個值類型(如Int32)傳入時,需要裝箱。 另一種用法是,一個非泛型的容器,同樣是為了保證通用,而將元素類型定義為Object。於是,要將值類型數據加入容器時,需要裝箱。

      重點:拆箱只是獲取引用對象中指向值類型部分的指針,而內容拷貝則是賦值語句之觸發。

      注:使用裝箱/拆箱會有風險,也會有一定的性能影響,一般可以用泛型替代。

 

三、泛型

      為什么要用泛型?

      通俗:為合並參數類型不同,邏輯相同的方法或類,提高代碼復用率而產生的。

      正規:可以將類型參數化以達到代碼復用提高軟件開發工作效率(增大代碼復用性),並且提高性能和降低裝箱、拆箱的成本或風險

      泛型有哪些優點?

      使用泛型類型可以最大限度地重用代碼、提高性能;可避免引入運行時強制轉換或裝箱操作的成本或風險;類型安全的(聲明T是string類型,就不能是decimal類型)

      應用:接口返回實體類用到(如:微信,QQ等三方接口);持久層實體映射(Orm框架中,基本上都是用實體做數據映射)等功能。

     

四、List是什么,有什么優點?

   List類是ArrayList類的泛型等效類,它的大部分用法都與ArrayList相似,因為List類也繼承了IList接口。在聲明List集合時,我們同時需要為其聲明List集合內數據的對象類型,彌補了ArrayList存在不安全類型與裝箱拆箱的缺點

    優點:類型安全;性能增強;代碼復用。

       

五、抽象類和接口關系

      抽象類和接口有什么相似處?

  1. 抽象類和接口類都不能直接實例化;
  2. 都包含未實現的方法聲明;
  3. 派生類必須實現未實現的方法,抽象類是抽象方法,接口則是所有成員(方法,屬性,索引)。

      抽象類和接口有什么區別?

  1. 抽象類更多的是定義在一系列緊密相關的類間,而接口大多數是關系疏松都實現某一功能的類中;

  2. 接口基本上不具備繼承的任何具體特點,它只是定義了能夠調用的方法;

  3. 一個類可以繼承多個接口,只能繼承一個類;抽象類可以繼承接口、抽象類;接口可以繼承接口,不能繼承抽象類;

  4. 接口可以用於支持回調,而繼承並不具備這個特點;抽象類不能被密封;

  5. 抽象類實現的具體方法默認為虛的,但實現接口的類中的接口方法卻默認為非虛的,當然您也可以聲明為虛的;

  6. 抽象把可變的與不可變的分離。接口就是定義為不可變的,而把可變的給子類去實現(抽象是相同中找不同,接口是不同種找相同);

  7. 好的接口定義應該是具有專一功能性的,而不是多功能的,否則造成接口污染(EG:IDispose,IApiController,IEnumerable)。如果一個類只是為了實現這個接口的中一個功能,而不得不去實現接口中的其他方法,就造成接口污染

  8. 盡量避免使用繼承來實現組建功能,而是使用黑箱復用,即對象組合。因為繼承的層次增多,造成最直接的后果就是當你調用這個類群中某一類,就必須把他們全部加載到棧中!后果可想而知。使用某個類的對象來調用另外的類的方法和屬性,可以增加代碼的靈活性、復用性和擴展性;

      抽象類和接口使用在什么環境使用?

  1. 如果預計要創建組件的多個版本,則創建抽象類;

  2. 如果創建的功能將在大范圍的全異對象間使用,要設計小而簡練的功能塊,則使用接口;

  3. 如果要設計大的功能單元,要在組件的所有實現間提供通用的已實現功能,則使用抽象類;

  4. 抽象類主要用於關系密切的對象;而接口適合為不相關的類提供通用功能。

      引用博客園中常見例子:

      1.狗和貓,他們都會叫、跑、睡覺,就繼承同一個行為接口“行為”;但是加菲貓和折耳貓屬於“貓”抽象類,二哈和柯基屬於“狗”抽象類。

      2.公雞和母雞屬於雞(抽象類),不屬於鴨(單繼承),直接要一個雞是要犯法的(不能直接實例化),如果需要具體的公雞、母雞是可以獲得的。公雞、母雞都毛、兩只腳(接口實現)。

 

六、什么是面向對象

      單從字面上說,就是以對象為起點的編程思想。由於傳統面向過程的編程方式,在各個功能模塊的耦合性比較強,代碼的重復率太高,造成軟件后期迭代擴展功能很不方便,缺乏靈活性和代碼重用性。以至於越往后期,代碼的可維護性越差。為了提高代碼的可維護性,重用性,靈活性和擴展性,面向對象誕生了。

      面向對象三大特征,封裝,繼承,多態。用我做過的一個三方登錄功能來說明:功能要求用戶可用通過PC客戶端,PC網頁端和APP端選者微信,QQ或直接輸入賬號密碼登錄。其中有兩個難點,第一個就是涉及到3種客戶端,第二個就是三方登錄平台多個。這種情況下就得用到面向對象的三大特性。首先:PC客戶端,PC網頁端和APP端是3個不同的登錄邏輯,用封裝特性可以將3種客戶端的功能分別封裝起來,降低耦合性,便於后期各個內部功能拓展。但是考慮到3種客戶端登錄功能又有許多可以公用的方法,可以用繼承特性,將公用的方法給單獨封裝起來,作為一個父類(修飾符一般用protected)大家都集成它。每種客戶端類型登錄中又有多重三方登錄,這里也可以用到封裝和繼承。在登錄功能中,會有和其他服務交互的功能,和每個服務交互的功能又有不同的區別。這里就可以用到多態,這里可以用一個簡單工廠來實現多態,根據不同請求,與不同服務器做交互。

 

七、委托和事件

      什么是委托,什么是多播委托,有那些優點,什么地方用到?

  1. 委托是一種引用類型,它定義了方法的類型,使得可以將方法當作另一個方法的參數來進行傳遞。是一種安全地封裝方法的類型,它與 C 和C++ 中的函數指針類似。
  2. 多播委托是一個由委托串成的鏈表,當鏈表上的一個委托被回調時,所有鏈表上該委托的后續委托將會被順序執行。要注意的是,多播委托必須是同類型的,返回類型必須是void,並且不能帶輸出參數(但可以帶引用參數)。
  3. 委托可以引用實例和靜態方法,而函數指針只能引用靜態方法。在定義委托的時候,需要定義返回類型,參數類型和個數,所以委托相對於指針是類型安全和保險的
  4. 委托可以用在數據持久化處理中(數據實體映射);在某些情況下,可以替代工廠模式。
  5. public delegate void OperateDelegate(string name);
    public void OperatePeople(string name, OperateDelegate operateDelegate) {
                   operateDelegate(name);
    }

      什么是事件機制,有那些優點?

  1. 事件是某對象在發生其關注的事情時用來提供通知的一種方式,通俗的講就是:監控+通知,有點像多播委托,但是沒有同步,異步功能。
  2. 事件是單獨開啟線程監控程序,不需要主程序等待事件發生,減少了資源消耗;使用事件,可以很方便地確定程序執行順序;
  3. 符合面向對象封裝思想。
    public class EventManager
    {
    //這一次我們在這里聲明一個事件
    public event EventDelegate MakeGreet;
    
    public void EventOpt(string talkStr, ref string retStr)
     {
      MakeGreet(talkStr,ref retStr);
     }
    }

      參考文章:面試中的委托面試中的事件委托與事件

 

八、變體、協變性、逆變性、不變性

定義

變體:帶有協變或逆變參數的泛型接口或委托(可隱式轉換);

協變性,子到父的轉換,讓一個帶有協變參數的泛型接口或泛型委托可以接收類型更具體的泛型接口或泛型委托作為參數,可以看成面向對象中多態的一個延伸

逆變性,父到子的轉換;讓一個帶有協變參數的泛型接口或泛型委托可以接收類型泛化的泛型接口泛型委托作為參數,實際上是參數類型更加精細化的過程。

不變性,只能是引用類型的泛型接口或泛型委托才能做協變/逆變操作。如果不用關鍵字(out或in),強制轉換都不行。

總結:協變類型參數可用作返回類型,而逆變類型參數可用作參數類型。

為什么要用可變性?

可變性可以安全的將一種類型轉換成另一種類型。

 

九、Lock的作用

      lock的作用,怎么使用?

      lock 關鍵字可確保當一個線程位於代碼的臨界區時,另一個線程不會進入該臨界區。 如果其他線程嘗試進入鎖定的代碼,則它將一直等待(即被阻止),直到該對象被釋放。

      1、lock的是引用類型的對象,string類型除外。

      2、lock推薦的做法是使用靜態的、只讀的、私有的對象(靜態能保證每次進入的只有一個線程,共有的有被其他程序干擾的風險,如果lock的對象在外部改變了,對其他線程就會暢通無阻,失去了lock的意義)

      3、適用於單台服務器環境。

 

十:單例模式

    上文說了Lock,順便說一下單例模式。

    為什么需要單例模式?多線程下怎么確保目標類實例唯一?

    為了保證在整個應用程序的生命周期中,在任何時刻,被指定的類只有一個實例,並為客戶程序提供一個獲取該實例的全局訪問點。在多線程下,如果只是常規寫法(兩個私有+一個只判空的共有)是無法避免某時刻目標類只有

一個實例存在(當多個線程同時通過判空判斷),在判空判斷中加Lock可以解決這個問題。

  1. if (instance == null)
    {
       lock (locker)
       {
        //如果類的實例不存在則創建,否則直接返回
         if(instance == null)
         {
          instance = new singleClass();
         }
        }
      }
    }

     注:單例如果使用了,就會在程序的整個生命周期中存在,如果過多使用會造成空間資源浪費。

           單例是為了確保某時刻只有一個實例存在,不要在某些允許同時有多個實例的地方使用,比如多數據庫操作。

 

     

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM