隨著培訓機構的增多,越來越多的人進入IT行業。那麼對於我們這些自學出來,經驗不夠豐富的轉行者來說,我們需要掌握最起碼的一些東西,這對於面試很有用,而且在工作中也很常用。本人在學習了網上各位大神關於一些知識點的總結和研究後,總結了這麼一套東西。祝大家都能工作順利,找到自己滿意的工作。 重寫:當一個子類 ...
隨著培訓機構的增多,越來越多的人進入IT行業。那麼對於我們這些自學出來,經驗不夠豐富的轉行者來說,我們需要掌握最起碼的一些東西,這對於面試很有用,而且在工作中也很常用。本人在學習了網上各位大神關於一些知識點的總結和研究後,總結了這麼一套東西。祝大家都能工作順利,找到自己滿意的工作。
- 重載和重寫
重寫:當一個子類繼承一父類,而子類中的方法與父類中的方法的名稱,參數個數、類型都完全一致時,就稱子類中的這個方法重寫了父類中的方法。
重載:一個類中的方法與另一個方法同名,但是參數表不同,這種方法稱之為重載方法。
- 面向對象
封裝:那麼封裝是什麼呢? 封裝就是這個人要完成一件事情,他所需要的任何工具都帶在了自己的身上,所需要的技術也都裝在了自己的腦子裡了。 每個對象都包含它能進行操作所需要的所有信息,因此對象不必依賴其它的對象來完成自己的操作
繼承:
實例化:
(1)、先父類後子類,先靜態後成員;
實例化的時候先調用父類的靜態構造快,在調用父類的構造方法,然後子類的構造塊,在調用子類的構造方法;
(2) 、預設調用父類空構造;
重寫 override
(1)、重寫與重載:
重寫:繼承的子類中,方法簽名相同( 方法名+形參個數 類型 順序 )
重載:同一個類 方法名相同 ,形參個數 類型 順序 不同
(2)、重寫規則:在子類中簽名與父類中相同,在合理範圍內提高子類可見性;
A、返回類型:基本類型和void必須相同;引用類型要<=父類的返回類
B、異常:
C、可見性:要大於或等於父類中被重寫的方法
多態:
有多態之前必須要有繼承,只有多個類同時繼承了同一個類,才有多態這樣的說法。
同樣一個方法,子類都有不同的實現方式,這個就是多態了,多態有助於程式的靈活性。
子類中如果重寫了父類的方法(多態),那麼父類中的這個方法將不會再調用。
編譯時多態:其實就是重載,是對非虛成員來說的,系統在編譯時,根據不同簽名來決定實現何種操作。
運行時多態:也就是重寫,是通過虛成員實現的,指直到系統運行時,才根據實際情況決定實現何種操作。
第二種情況是在繼承的基礎上實現的,子類繼承基類時,通過對虛成員的重寫,然後利用基類引用子類對象,那麼不同的子類對象實現相應的不同操作。
這樣的好處是顯而易見的,利用基類類型定義一次,然後給它傳入不同的子類對象,然後實現不同的操作,提高了效率。
3.值類型和引用類型
值類型: |
byte,short,int,long,float,double,decimal,char,bool 和 struct 統稱為值類型。 |
引用類型: |
string 和 class統稱為引用類型。 |
1.值類型存儲在記憶體棧中,引用類型數據存儲在記憶體堆中,而記憶體單元中存放的
是堆中存放的地址。
2.值類型存取快,引用類型存取慢。
3.值類型表示實際數據,引用類型表示指向存儲在記憶體堆中的數據的指針和引用。
4.棧的記憶體是自動釋放的,堆記憶體是.NET 中會由 GC 來自動釋放。
5.值類型繼承自 System.ValueType,引用類型繼承自 System.Object。
4.數據結構
數組Array:連續存儲
數組是最簡單的數據結構。其具有如下特點:
數組存儲在連續的記憶體上。
數組的內容都是相同類型。
數組可以直接通過下標訪問。
int size = 5;
int[] test = new int[size];
//賦值
test2[0] = "chen";
//修改
test2[0] = "chenjd";
聲明一個新的數組時,必須指定其長度
ArrayList:
為瞭解決數組創建時必須指定長度以及只能存放相同類型的缺點而推出的數據結構。可以存儲不同類型的元素。這是由於ArrayList會把它的元素都當做Object來處理。因而,加入不同類型的元素是允許的。
ArrayList test3 = new ArrayList();
//新增數據
test3.Add("chen");
test3.Add("j");
test3.Add("d");
test3.Add("is");
test3.Add(25);
//修改數據
test3[4] = 26;
//刪除數據
test3.RemoveAt(4);
所謂裝箱 (boxing):就是值類型實例到對象的轉換那麼拆箱:就是將引用類型轉換為值類型。
List<T>泛型List
為瞭解決ArrayList不安全類型與裝箱拆箱的缺點,所以出現了泛型的概念,作為一種新的數組類型引入。和ArrayList很相似,長度都可以靈活的改變,最大的不同在於在聲明List集合時,我們同時需要為其聲明List集合內數據的對象類型
List<string> test4 = new List<string>();
Dictionary<K,T>
字典的實現方式就是哈希表的實現方式,當創建字典時,必須聲明key和item的類型,我們新建了一個空的字典,那麼伴隨而來的是2個長度為3的數組。
- 介面和抽象類
C#基礎之介面
介面的定義是指定一組函數成員而不實現成員的引用類型,其它類型和介面可以繼承介面。
(1) 通過介面可以實現多重繼承,C#介面的成員不能有public、protected、internal、private等修飾符。原因很簡單,介面裡面的方法都需要由外面介面實現去實現方法體,那麼其修飾符必然是public。C#介面中的成員預設是public的,java中是可以加public的。
(2) 介面成員不能有new、static、abstract、override、virtual修飾符。有一點要註意,當一個介面實現一個介面,這2個介面中有相同的方法時,可用new關鍵字隱藏父介面中的方法。
(3) 介面中只包含成員的簽名,介面沒有構造函數,所有不能直接使用new對介面進行實例化。介面中只能包含方法、屬性、事件和索引的組合。介面一旦被實現,實現類必須實現介面中的所有成員,除非實現類本身是抽象類。
(4) C#是單繼承,介面是解決C#裡面類可以同時繼承多個基類的問題。
class Program
{
static void Main(string[] args)
{
IWorker james1 = new James1();
IWorker james2 = new James2();
james1.work("設計");
james2.work("編程");
//從這個例子我體會到了有介面的好處,可以想象如果又來了新的員工。
//如果不採用介面,而是每個員工都有一個單獨的類,這樣就會容易出錯。
//如果有介面這種協議約束的話,那麼只要實現了介面就肯定有介面里聲明的方法,我們只需拿來調用。 }
}
public interface IWorker{ void work(string s); }
class James1 : IWorker
{
public void work(string s)
{
Console.WriteLine("我的名字是James1,我的工作是" +s);
}
}
class James2 : IWorker
{
public void work(string s)
{
Console.WriteLine("我的名字是James2,我的工作是"+s);
}
}
修飾符 interface 介面名
{
//介面主體
}
1、一個介面就相當於一個抽象類,但是它不能半喊任何實現方法。
2、介面的每種方法都必須在派生類中實現。
3、介面有時候可以看成是類的模具,它指明一個類該提供哪些內容。
4、介面主體只限於方法、索引器、屬性的聲明。
5、介面中不能包含欄位、構造函數和常量等。
6、介面成員是隱式公開的,如果對其顯式指定訪問級別,就會出現編譯器錯誤。
7、在介面中不能實現任何方法,屬性或者索引器。
8、在指定方法時,只需給出返回類型、名稱和參數列表,然後以分號結束。
9、實現介面的語法與實現繼承一樣,都用冒號“:”
示例
interface Icustomer
{
………………
}
public class MyClass: Icustomer
{
………………
}
10、介面中的方法不能重寫,只能實現。
11、
編碼標準:
介面名稱需始終冠以大寫字母I
什麼是抽象類:不能被實例化的類稱為抽象類,抽象類是派生類的基類。
abstract class 類名
{
…………
}
1、一個抽象類可以同時包含抽象方法和非抽象方法。
2、抽象方法只在派生類中真正實現,這表明抽象方法只存放函數原型,不涉及主體代碼,
3、派生自抽象類的類需要實現其基類的抽象方法,才能實例化對象。
4、使用override關鍵子可在派生類中實現抽象方法,經override聲明重寫的方法稱為重寫基類方法,其簽名必須與override方法的簽名相同。
示例:
using System;
namespace Example_5
{
//抽象類
abstract class ABC
{
//抽象方法
public abstract void Afunc();
}
//派生類
class Derv:ABC
{
//實現抽象類中的抽象方法
public override void Afunc()
{
Console.WriteLine(“實現抽象方法”);
}
}
public class Test
{
static void Main(string[] args)
{
Derv obj=new Derv();
obj.Afunc();
}
}
}
5、基類實現抽象類,則派生類不需要重新實現該抽象類。
6、抽象類並不僅僅只是一種實現技巧,它更代表一種抽象的概念,從而為所有的派生類確立一種約定。
介面和抽象類的區別
介面用於規範,抽象類用於共性。抽象類是類,所以只能被單繼承,但是介面卻可以一次實現多個。
介面中只能聲明方法,屬性,事件,索引器。而抽象類中可以有方法的實現,也可以定義非靜態的類變數。
抽象類可以提供某些方法的部分實現,介面不可以。抽象類的實例是它的子類給出的。介面的實例是實現介面的類給出的。
在抽象類中加入一個方法,那麼它的子類就同時有了這個方法。而在介面中加入新的方法,那麼實現它的類就要重新編寫(這就是為什麼說介面是一個類的規範了)。
介面成員被定義為公共的,但抽象類的成員也可以是私有的、受保護的、內部的或受保護的內部成員(其中受保護的內部成員只能在應用程式的代碼或派生類中訪問)。此外介面不能包含欄位、構造函數、析構函數、靜態成員或常量。
- 委托和事件
委托:可以理解為將一個方法變成了參數類型,屬性。下麵,我們直接上代碼,在代碼中理解委托的運用和定義。
namespace Delegate {
//定義委托,它定義了可以代表的方法的類型
public delegate void GreetingDelegate(string name);
class Program {
Private static void EnglishGreeting(string name) {
Console.WriteLine("Morning, " + name);
}
private static void ChineseGreeting(string name)
{
Console.WriteLine("早上好, " + name);
}
//註意此方法,它接受一個GreetingDelegate類型的方法作為參數
private static void GreetPeople(string name, GreetingDelegate MakeGreeting)
{
MakeGreeting(name); }
static void Main(string[] args)
{
GreetPeople("Jimmy Zhang", EnglishGreeting); GreetPeople("張子陽", ChineseGreeting); Console.ReadKey(); }
委托是一個類,它定義了方法的類型,使得可以將方法當作另一個方法的參數來進行傳遞,這種將方法動態地賦給參數的做法,可以避免在程式中大量使用If-Else(Switch)語句,同時使得程式具有更好的可擴展性。
事件:它封裝了委托類型的變數,使得:在類的內部,不管你聲明它是public還是protected,它總是private的。在類的外部,註冊“+=”和註銷“-=”的訪問限定符與你在聲明事件時使用的訪問符相同。
聲明一個事件不過類似於聲明一個進行了封裝的委托類型的變數而已。
7.設計模式
單例模式
確保一個類只有一個實例,並提供一個全局訪問點。
- 通過定義一個靜態私有變數來記錄單例類的唯一實例。
- 私有方法來防止外界使用new關鍵字來創建該類實例。
- 公有方法來提供該類實例的唯一全局訪問點
- 假如實例不存在則new一個新的實例,否則返回已有實例。
對於多線程來說則定義一個線程標識保證線程同步,接著加一個線程鎖,多一句判斷就行了。
觀察者模式
Observer設計模式中主要包括如下兩類對象:
- Subject:監視對象,它往往包含著其他對象所感興趣的內容。
- Observer:監視者,它監視Subject,當Subject中的某件事發生的時候,會告知Observer,而Observer則會採取相應的行動。
Observer設計模式是為了定義對象間的一種一對多的依賴關係,以便於當一個對象的狀態改變時,其他依賴於它的對象會被自動告知並更新。Observer模式是一種松耦合的設計模式。
下麵我們開始上代碼:
8.排序演算法
冒泡排序:
快速排序:
一趟快速排序的演算法是:
1)設置兩個變數i、j,排序開始的時候:i=0,j=N-1;
2)以第一個數組元素作為關鍵數據,賦值給key,即key=A[0];
3)從j開始向前搜索,即由後開始向前搜索(j–),找到第一個小於key的值A[j],將A[j]和A[i]互換;
4)從i開始向後搜索,即由前開始向後搜索(i++),找到第一個大於key的A[i],將A[i]和A[j]互換;
5)重覆第3、4步,直到i=j; (3,4步中,沒找到符合條件的值,即3中A[j]不小於key,4中A[i]不大於key的時候改變j、i的值,使得j=j-1,i=i+1,直至找到為止。找到符合條件的值,進行交換的時候i, j指針位置不變。另外,i==j這一過程一定正好是i+或j-完成的時候,此時令迴圈結束)。
插入排序:
⒈ 從第一個元素開始,該元素可以認為已經被排序
⒉ 取出下一個元素,在已經排序的元素序列中從後向前掃描
⒊ 如果該元素(已排序)大於新元素,將該元素移到下一位置
⒋ 重覆步驟3,直到找到已排序的元素小於或者等於新元素的位置
⒌ 將新元素插入到下一位置中
⒍ 重覆步驟2~5
選擇排序:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小(大)元素,然後放到已排序序列的末尾。以此類推,直到所有元素均排序完畢。
選擇排序的主要優點與數據移動有關。如果某個元素位於正確的最終位置上,則它不會被移動。選擇排序每次交換一對元素,它們當中至少有一個將被移到其最終位置上,因此對n個元素的表進行排序總共進行至多n-1次交換。在所有的完全依靠交換去移動元素的排序方法中,選擇排序屬於非常好的一種。
9.線程
1、基本概念
進程(Process)是Windows系統中的一個基本概念,它包含著一個運行程式所需要的資源。一個正在運行的應用程式在操作系統中被視為一個進程,進程可以包括一個或多個線程。線程是操作系統分配處理器時間的基本單元,在進程中可以有多個線程同時執行代碼。進程之間是相對獨立的,一個進程無法訪問另一個進程的數據(除非利用分散式計算方式),一個進程運行的失敗也不會影響其他進程的運行,Windows系統就是利用進程把工作劃分為多個獨立的區域的。進程可以理解為一個程式的基本邊界。是應用程式的一個運行常式,是應用程式的一次動態執行過程。
線程(Thread)是進程中的基本執行單元,是操作系統分配CPU時間的基本單位,一個進程可以包含若幹個線程,在進程入口執行的第一個線程被視為這個進程的主線程。在.NET應用程式中,都是以Main()方法作為入口的,當調用此方法時系統就會自動創建一個主線程。線程主要是由CPU寄存器、調用棧和線程本地存儲器(Thread Local Storage,TLS)組成的。CPU寄存器主要記錄當前所執行線程的狀態,調用棧主要用於維護線程所調用到的記憶體與數據,TLS主要用於存放線程的狀態信息。
創建多線程的步驟:
1、編寫線程所要執行的方法
2、實例化Thread類,並傳入一個指向線程所要執行方法的委托。(這時線程已經產生,但還沒有運行)
3、調用Thread實例的Start方法,標記該線程可以被CPU執行了,但具體執行時間由CPU決定
10.協同程式
一。什麼是協同程式
協同程式,即在主程式運行時同時開啟另一段邏輯處理,來協同當前程式的執行。換句話說,開啟協同程式就是開啟一個線程。
二。協同程式的開啟與終止
在Unity3D中,使用MonoBehaviour.StartCoroutine方法即可開啟一個協同程式,也就是說該方法必須在MonoBehaviour或繼承於MonoBehaviour的類中調用。
在Unity3D中,使用StartCoroutine(string methodName)和StartCoroutine(IEnumerator routine)都可以開啟一個線程。區別在於使用字元串作為參數可以開啟線程併在線程結束前終止線程,相反使用IEnumerator 作為參數只能等待線程的結束而不能隨時終止(除非使用StopAllCoroutines()方法);另外使用字元串作為參數時,開啟線程時最多只能傳遞 一個參數,並且性能消耗會更大一點,而使用IEnumerator 作為參數則沒有這個限制。
在Unity3D中,使用StopCoroutine(string methodName)來終止一個協同程式,使用StopAllCoroutines()來終止所有可以終止的協同程式,但這兩個方法都只能終止該 MonoBehaviour中的協同程式。
還有一種方法可以終止協同程式,即將協同程式所在gameobject的active屬性設置為false,當再次設置active為ture時,協同程 序並不會再開啟;如是將協同程式所在腳本的enabled設置為false則不會生效。這是因為協同程式被開啟後作為一個線程在運行,而 MonoBehaviour也是一個線程,他們成為互不幹擾的模塊,除非代碼中用調用,他們共同作用於同一個對象,只有當對象不可見才能同時終止這兩個線 程。
粗淺小結:
1.Coroutines顧名思議是用來協助主要進程的,在Unity中感覺就是一個可動態添加和移除的Update()函數。它的調用在所有Update函數之後。
2.yield就像是一個紅綠燈,在滿足緊跟在它後面的條件之前,這個協程會掛起,把執行權交給調用它的父函數,滿足條件時就可以執行yield下麵的代碼。
協同的用法
Yield中斷:(有中斷就代表程式停在該處,等待yield後的時間結束再繼續執行後面的語句。)
在unity C#中yield(中斷)語句必須要在IEnumerator類型里
C#方法,方法的返回類型為IEnumerator,返回值如(eg: yield return new WaitForSeconds(2); 或者 yield return null;)。
yields不可以在Update或者FixedUpdate里使用。
Coroutine協同:(為什麼要有Coroutine的概念? 因為有中斷的存在,試想一個程式都靠Yield來暫停的話,如何做到一個事件暫停的過程中,暫停過程中繼續執行後面的程式? 那麼就要靠Coroutine來實現。)
- 可以在UPdate或者FixedUpdate里使用coroutine
- 這裡有兩種:StartCoroutine(協同工作) 和 yield return StartCoroutine(中斷式的協同工作)
- 有yield的代表先執行完本語句(不管多長時間),或者執行完本yield方法調用,才執行後續語句。例如StartCoroutine(WaitAndPrint(2.0F)),繼續執行StartCoroutine後面的語句,
- 沒有yield的代表繼續順序執行。例如:yield return StartCoroutine(WaitAndPrint(2.0F)),代表StartCoroutine(WaitAndPrint(2.0F))函數里等待全部執行完,再執行StartCoroutine後面的語句
using UnityEngine;using System.Collections;
public class example : MonoBehaviour {
IEnumerator Start() {
print("Starting " + Time.time);
yield return StartCoroutine(WaitAndPrint(2.0F));
print("Done " + Time.time);
}
IEnumerator WaitAndPrint(float waitTime) {
yield return new WaitForSeconds(waitTime);
print("WaitAndPrint " + Time.time);
}
}
using UnityEngine;using System.Collections;
public class example : MonoBehaviour {
void Start() {
print("Starting " + Time.time);
StartCoroutine(WaitAndPrint(2.0F));
print("Before WaitAndPrint Finishes " + Time.time);
}
IEnumerator WaitAndPrint(float waitTime) {
yield return new WaitForSeconds(waitTime);
print("WaitAndPrint " + Time.time);
}
}
using UnityEngine;using System.Collections;
public class example : MonoBehaviour {
IEnumerator Do() {
print("Do now");
yield return new WaitForSeconds(2);
print("Do 2 seconds later");
}
IEnumerator Awake() {
yield return StartCoroutine("Do"); //Yield StartCoroutine就代表中斷式的協同工作
print("Also after 2 seconds");
print("This is after the Do coroutine has finished execution");
}
}
using UnityEngine;using System.Collections;
public class example : MonoBehaviour {public static IEnumerator Do() {
print("Do now");
yield return new WaitForSeconds(2);
print("Do 2 seconds later");
}void Awake() {
Do(); //執行DO,但是do後的語句繼續執行
print("This is printed immediately");
}