類的定義
- 面向?qū)ο缶幊?,結(jié)構(gòu)化編程
- 類,定義了每一個(gè)對(duì)象
- 類,就是創(chuàng)造對(duì)象的模板,數(shù)據(jù)+功能
- 類的成員,數(shù)據(jù)成員+函數(shù)成員
4.1 數(shù)據(jù)成員:是包含類的數(shù)據(jù)(字段+常量和事件的成員)
4.2 函數(shù)成員:提供了操作類中數(shù)據(jù)的某些功能(方法+屬性+構(gòu)造方法+終結(jié)器(析構(gòu)方法)+運(yùn)算符+索引器) - 類的字段和方法
5.1 字段,就是數(shù)據(jù)成員(訪問修飾符,類型,字段名稱)
5.2 方法,就是函數(shù)成員(訪問修飾符,返回值類型,方法名稱) - 求平方是double類型,一般會(huì)把他轉(zhuǎn)換為float類型,強(qiáng)行轉(zhuǎn)換
- 類中,編程規(guī)范上,一般把字段設(shè)置成privte,那就只能在單一的類中使用,所以需要用個(gè)Set+this.去重新設(shè)置,以保安全
構(gòu)造過程
- 我們構(gòu)造對(duì)象的時(shí)候,對(duì)象的初始化過程是自動(dòng)完成的,但是在初始化對(duì)象的過程中有的時(shí)候需要做一些額外的工作,例如需要初始化對(duì)象存儲(chǔ)的數(shù)據(jù),構(gòu)造函數(shù)就是用于初始化數(shù)據(jù)的函數(shù)。
...
public class MyClass{
public MyClass(){
這個(gè)構(gòu)造函數(shù)的函數(shù)體
}
}
...
當(dāng)我們使用new關(guān)鍵字創(chuàng)建類的時(shí)候,就會(huì)調(diào)用構(gòu)造方法。我們一般會(huì)是用構(gòu)造方法進(jìn)行初始化數(shù)據(jù)的一些操作。構(gòu)造函數(shù)可以進(jìn)行重載,跟普通函數(shù)重載是一樣的。
注意:
當(dāng)我們不寫,任何構(gòu)造函數(shù)的時(shí)候,編譯器會(huì)提供給我們一個(gè)默認(rèn)的無參的構(gòu)造函數(shù),但是如果我們定義了一個(gè)或者多個(gè)構(gòu)造函數(shù),編譯器就不會(huì)再提供默認(rèn)的構(gòu)造函數(shù)。
屬性的定義
- 演示
...
public int MyIntProperty
{
get
{
Console.WriteLine("屬性中的get塊被調(diào)用");
return 100;
}
set
{
Console.WriteLine("屬性中的set塊被調(diào)用");
Console.WriteLine("在set塊中訪問value的值是:" + value);
}
}
...
set值:當(dāng)給屬性設(shè)置值的時(shí)候,會(huì)調(diào)用set方法
get值:當(dāng)給屬性取值的時(shí)候,會(huì)調(diào)用get方法
兩者不要求同時(shí)存在,如果沒有g(shù)et塊,那就不能通過屬性取值了。 - get+set的作用:
1)通過set方法,在設(shè)置值之前,做一些校驗(yàn)的工作。
2)可以設(shè)置只讀和只寫,在set或get那,用private / public
- Public string Name{get;set;}
匿名類型
- 用引用來添加其他庫的類別
- 只是要public一次
- 當(dāng)var,匿名類型被賦值了以后,那他的形式就被確定下來了,不能再變
堆和棧 程序運(yùn)行時(shí)的內(nèi)存區(qū)域
我們把內(nèi)存分為堆空間和棧空間
1)棧空間比較小,但讀取速度快
2)堆空間比較大,但讀取速度慢棧的特征(先入后出)
1)數(shù)據(jù)只能從棧的頂端插入,和刪除
2)把數(shù)據(jù)放入棧頂,稱為入棧(push)
3)把棧頂刪除數(shù)據(jù)稱為出棧(pop)堆的特征
1)堆是一塊內(nèi)存區(qū)域,與棧不同,堆里的內(nèi)存能夠以任意順序存入和移除.NET當(dāng)中的GC Garbage Collection 垃圾回收器
CLR的CG就是內(nèi)存管理機(jī)制,我們寫程序不需要關(guān)心內(nèi)存的使用,因?yàn)檫@些都是CLR幫我們做了
過程:
1)程序在堆里保存了三個(gè)對(duì)象
2)后來的程序中,其中的一個(gè)對(duì)象不再被程序使用
3)垃圾收集器發(fā)現(xiàn)無主對(duì)象并釋放他
4)垃圾收集之后,被釋放對(duì)象的內(nèi)存可以被重用
值類型和引用類型,在內(nèi)存中的存儲(chǔ)
類型被分為兩種,值類型(整數(shù)+bool+struct+char+小數(shù))+引用類型(string,數(shù)組,自定義的類,內(nèi)置的類)
值類型,只需要一段內(nèi)存,用于存儲(chǔ)實(shí)際的數(shù)據(jù)(單獨(dú)定義的時(shí)候,放在棧中)
引用類型,需要兩段內(nèi)存,第一段存儲(chǔ)實(shí)際數(shù)據(jù),他總是在堆中。第二段是一個(gè)引用,指向數(shù)據(jù)在堆中的存放位置(指向第一段內(nèi)存)
當(dāng)我們使用了引用類型賦值的時(shí)候,其實(shí)是賦值的是引用類型。賦值的話,其實(shí)是調(diào)換內(nèi)存地址的指向
-
//如果數(shù)組是一個(gè)值類型的數(shù)組,那么數(shù)組中直接存儲(chǔ)數(shù)值。
//如果是一個(gè)引用類型的數(shù)組,那么數(shù)組中存儲(chǔ)的是引用地址。
...
static void Test1()
{
int i = 34;
int j = 34;
int temp = 34;
char c = 'a';
bool b = true;
}
...
Test1
...
static void Test2()
{
int i = 34;
int j = 234;
string name = "siki";
}
...
Test2
...
static void Test3()
{
string name = "siki";
string name2 = "taikr";
name = name2;
name = "google";
Console.WriteLine(name + ":" + name2);
}
...
Test3
...
static void Test4()
{
Vector3 v = new Vector3();
v.x = 100;
v.y = 100;
v.z = 100;
Vector3 v2 = new Vector3();
v2.x = 200;
v2.y = 200;
v2.z = 200;
v2 = v;
v2.x = 300;
Console.WriteLine(v.x);
}
class Vector3
{
public float x, y, z;
}
...

...
static void Test5()
{
Vector3[] vArray = new Vector3[] { new Vector3(), new Vector3(), new Vector3() };
//如果數(shù)組是一個(gè)值類型的數(shù)組,那么數(shù)組中直接存儲(chǔ)數(shù)值。
//如果是一個(gè)引用類型的數(shù)組,那么數(shù)組中存儲(chǔ)的是引用地址。
Vector3 v1 = vArray[0];
vArray[0].x = 100;
v1.x = 200;
Console.WriteLine(vArray[0].x);
}
...

繼承
實(shí)現(xiàn)繼承:
表示一個(gè)類型派生于一個(gè)基類型,他擁有該基類型的所有成員字段和函數(shù)。在實(shí)現(xiàn)繼承中,派生類型采用基類型的每個(gè)函數(shù)的實(shí)現(xiàn)代碼,除非在派生類型的定義中指定重寫某個(gè)函數(shù)的實(shí)現(xiàn)代碼。在需要給現(xiàn)有的類型添加功能或許多相關(guān)類型共享一組重要公共功能時(shí),這種類型的繼承非常有用。
接口繼承:
表示一個(gè)類型只繼承了函數(shù)的簽名,沒有繼承任何實(shí)現(xiàn)代碼,在需要指定該類型具有某些可用的特性時(shí),最好使用這種類型的繼承。
多重繼承:
使用多重繼承的有點(diǎn)是有爭議的:一方面,毫無疑問,可以使用多重繼承編寫非常復(fù)雜、但很緊湊的代碼。另一方面,使用多重實(shí)現(xiàn)繼承的代碼常常很難理解和調(diào)試。但是,C#的主要設(shè)計(jì)目標(biāo)是簡化健壯代碼,所以C#不支持多重實(shí)現(xiàn)繼承。
但是,C#允許類型派生自多個(gè)接口,多重接口繼承。這說明,C#類可以派生自另一個(gè)類和任意多個(gè)接口。更準(zhǔn)確地說,system.object是一個(gè)公共的基類,所以每個(gè)C#都有一個(gè)基類,還有可以有任意多個(gè)基接口。
...
class Program
{
static void Main(string[] args)
{
//Boss boss = new Boss();
//boss.AI();//繼承父類里面所有的數(shù)據(jù)成員和函數(shù)成員,都會(huì)繼承到子類里面
//boss.Attack();
//Enemy enemy;
//enemy = new Boss();//父類聲明的對(duì)象,可以用子類去構(gòu)造.不可以反過來
////enemy雖然使用父類進(jìn)行了聲明,但是使用了子類構(gòu)造,所以本質(zhì)上是一個(gè)子類類型,我們可以強(qiáng)制類型轉(zhuǎn)換,轉(zhuǎn)換成子類類型
//Boss boss = (Boss)enemy;
//boss.Attack();
Enemy enemy = new Enemy();
Boss boss = (Boss)enemy;//這樣會(huì)錯(cuò)誤,因?yàn)橐粋€(gè)對(duì)象是什么類型的,主要看他是什么構(gòu)造來定。這里的enemy使用了父類的構(gòu)造函數(shù),所以只有弗雷中的字段和方法,不能被強(qiáng)制轉(zhuǎn)換成子類
//可以使用父類去聲明對(duì)象,用子類去實(shí)例化
Console.ReadKey();
}
}
class Enemy
{
private float hp;
private float speed;
public float HP
{
get { return hp; }
set { hp = value; }
}
public float Speed
{
get { return speed; }
set { speed = value; }
}
public void AI()
{
Console.WriteLine("這里是Enemy1的公有AI方法");
}
public void Move()
{
Console.WriteLine("這里是Enemy1的公有move方法");
}
}
class Boss: Enemy
{
public void Attack()
{
AI();
Move();
HP = 100;//父類里面公有的數(shù)據(jù)和方法,才能被訪問
Console.WriteLine("Boss正在進(jìn)行攻擊");
}
}
...
虛方法
- 把一個(gè)基類函數(shù)聲明為virtual,就可以在任何派生類中重寫該函數(shù)
- 在派生類中,重寫另外一個(gè)函數(shù)時(shí),要使用override關(guān)鍵字顯示聲明
- 我們在子類里面重寫虛函數(shù)之后,不管在哪里調(diào)用都是用重寫之后的方法(只有當(dāng)通過子類的去構(gòu)造的時(shí)候,才會(huì)去調(diào)用子類的內(nèi)容)
- 虛方法重不重寫都沒關(guān)系,如果重寫了,就用新的,如果不重寫,就繼續(xù)調(diào)用虛方法
隱藏方法
- 如果簽名相同的方法在基類和派生類中都進(jìn)行了聲明,但是該方法沒有分別聲明為virtual和overri,派生類就會(huì)隱藏基類方法(要使用new方法進(jìn)行聲明)
- 隱藏方法:如果使用子類聲明的對(duì)象,調(diào)用隱藏方法會(huì)調(diào)用子類的,如果使用父類聲明對(duì)象,那么就會(huì)調(diào)用父類中的隱藏方法
- 一般少用隱藏方法,會(huì)引起混亂
this base關(guān)鍵字
- this的作用就是,可以去調(diào)用當(dāng)前類的各種方法(基類+當(dāng)前自己的),不用其實(shí)也可以,但是有this,會(huì)好找很多。
- base只能訪問父類里面的方法
詳細(xì)定義內(nèi)容:
- this可以訪問當(dāng)前類中定義的字段、屬性和方法,有沒有this都可以訪問,有this可以讓編譯器給出提示,另外當(dāng)方法的參數(shù)跟字段重名的時(shí)候,使用this可以表明訪問的是類中的字段。this可以調(diào)用父類中的方法和字段,有沒有this都可以方法,但是加上base.IDE工具會(huì)給出提示,可以把所有的都顯示出來
抽象類
- C#允許把類和函數(shù)聲明為abstract,抽象類不能實(shí)例化,抽象類可以包含普通函數(shù)和抽象函數(shù),抽象函數(shù)就是只有函數(shù)定義沒有函數(shù)體。顯然,抽象函數(shù)本身也是虛擬的virtual(只有函數(shù)定義,沒有函數(shù)體實(shí)現(xiàn))
- 類是一個(gè)模板,那么抽象類就是一個(gè)不完整的模板,我們不能使用不完整的模板去構(gòu)造對(duì)象
- 可以用抽象類去聲明對(duì)象,但是不用他去構(gòu)造
- 當(dāng)我們繼承了一個(gè)抽象類的時(shí)候,必須去實(shí)現(xiàn)抽象方法
密封類和密封方法
- C#允許把類和方法聲明為sealed,對(duì)于類,這表示不能繼承該類;對(duì)于方法表示不能重寫該方法
- 什么時(shí)候會(huì)使用密封類和密封方法?
答:防止重寫某些類導(dǎo)致代碼混亂,或,商業(yè)原因
...
class DerivedClass:BaseClass//密封類無法被繼承
{
public sealed override void Move()//我們可以把重寫的方法聲明為密封方法,表示該方法不能被重寫
{
base.Move();
}
}
//sealed class BaseClass//這里聲明了一個(gè)密封類
class BaseClass
{
public virtual void Move()
{
}
}
...
派生類的構(gòu)造函數(shù)
- 在子類中調(diào)用父類的默認(rèn)構(gòu)造函數(shù)(無參)(會(huì)先調(diào)用父類的,再調(diào)用子類的)
- 調(diào)用有參數(shù)的構(gòu)造函數(shù)
...
class Program
{
static void Main(string[] args)
{
//DerivedClass o1 = new DerivedClass();
DerivedClass o2 = new DerivedClass(1, 2);
Console.ReadKey();
}
}
class BaseClass
{
private int x;
public BaseClass()
{
Console.WriteLine("base class 構(gòu)造函數(shù)");
}
public BaseClass(int x)
{
this.x = x;
Console.WriteLine("y賦值完成");
}
}
class DerivedClass:BaseClass
{
private int y;
public DerivedClass()
//調(diào)用父類中無參的構(gòu)造函數(shù),當(dāng)我們沒有在子類的構(gòu)造函數(shù)中顯示聲明調(diào)用父類的構(gòu)造函數(shù),默認(rèn)會(huì)調(diào)用父類中的無參構(gòu)造函數(shù)
{
Console.WriteLine("這個(gè)是DerivedClass無參的構(gòu)造函數(shù)");
}
public DerivedClass(int x, int y) : base(x)
{
this.y = y;
Console.WriteLine("x賦值完成");
}
}
...
關(guān)于訪問修飾符
- class的修飾符,只有在class前面聲明了public,才能去其他類中訪問
- new:成員用相同的簽名隱藏繼承的成員
- static:成員不作用于類的具體實(shí)例
- virtual:成員可以由派生類重寫
- abstract:虛擬成員定義了成員的簽名,但沒有提供實(shí)現(xiàn)代碼
- override:成員重寫了繼承的虛擬或抽象成員
- sealed:對(duì)于類,不能繼承自密封類,對(duì)于屬性和方法,成員重寫已繼承的虛擬成員,但任何派生類中的人和成員都不能重寫該成員,該修飾符必須與override一起使用
- extern:成員在外用另一種語言實(shí)現(xiàn)
- protected:(可以應(yīng)用于類型和內(nèi)嵌類型的所有成員)只能派生的類型能訪問該項(xiàng)。當(dāng)沒有繼承的時(shí)候,protected和private是一樣的。但是如果是在繼承函數(shù)里面,繼承還是是可以訪問到projected的。
- static可以修飾字段/方法,修飾字段的時(shí)候,表示這個(gè)字段是靜態(tài)的數(shù)據(jù),叫做靜態(tài)字段或靜態(tài)屬性,修飾方法的時(shí)候,叫做靜態(tài)函數(shù)。使用static修飾的成員,只能通過類名訪問。
- 當(dāng)我們構(gòu)造對(duì)象的時(shí)候,對(duì)象中只包含了普通字段,不包含靜態(tài)字段(只能通過類名去訪問,通過類名去賦值和取值)
定義和實(shí)現(xiàn)接口
- 定義一個(gè)接口在語法上,跟定義一個(gè)抽象類完全相同,但不允許提供接口中人和成員的實(shí)現(xiàn)方式,一般情況下,接口只能包含方法、屬性、索引器和事件的聲明
- 接口不能有構(gòu)造函數(shù),也不能有字段,接口也不允許運(yùn)算符重載
- 接口定義中不允許聲明成員的修飾符,接口成員都是公有的
- 接口也是可以相互繼承的,只是在繼承過程中,可能會(huì)有方法的層級(jí)嵌套,這個(gè)都需要去聲明
列表List的創(chuàng)建和使用
- 集合類:列表List
- 當(dāng)我們有很多類型一樣的數(shù)據(jù)時(shí)候,前面我們一般使用數(shù)組來進(jìn)行管理,但是這樣有一個(gè)缺點(diǎn)就是數(shù)組的大小是固定的。如果我們很多類型一樣的數(shù)據(jù),比如游戲得分,我們可以集合類來進(jìn)行管理,比如列表List,我們可以使用List很方便地添加數(shù)據(jù),刪除數(shù)據(jù)還有其他對(duì)數(shù)據(jù)的操作。
- 類型總結(jié):
*創(chuàng)建列表:列表可以存儲(chǔ)任何類型的數(shù)據(jù),在創(chuàng)建列表對(duì)象的時(shí)候,首先要指定你要?jiǎng)?chuàng)建的這個(gè)列表要存儲(chǔ)什么類型(泛型)
List<int> scoreList = new List<int>();
new List<int>() { 1, 2, 3 };
new List<string>() { "one", "two" };
var newScoreList = new List<int>();
*往列表里注入數(shù)據(jù)
scoreList.Add(12);
scoreLis.Add(45);
*如何取得列表中的數(shù)據(jù)?列表中的數(shù)據(jù)跟數(shù)組有點(diǎn)相似,索引從0開始,可以通過索引來訪問
scoreList[0];//訪問添加到列表中的第一個(gè)數(shù)據(jù)
- 關(guān)于列表的更多內(nèi)容
1)列表初始容量為4,一旦發(fā)現(xiàn)超出范圍,就會(huì)擴(kuò)大為8,然后擴(kuò)大為16...依次增加
2)直接設(shè)置個(gè)值,就可以減少數(shù)組的創(chuàng)建和刪除,可以提高性能
3)可以通過Capacity屬性獲取和設(shè)置容量,intList.Capacity=100;
4)注意容量和列表中元素個(gè)數(shù)的區(qū)別,容量是列表中用于存儲(chǔ)數(shù)據(jù)的數(shù)組長度通過capacity獲取,列表中的元素是我們添加進(jìn)去需要管理的數(shù)據(jù),通過count獲取
列表的遍歷
var scoreList = new List<int>();
scoreList.Add(34);
scoreList.Add(334);
scoreList.Add(3344);
scoreList.Add(344);
scoreList.Add(32344);
scoreList.Add(134);
scoreList.Add(334);
//for (int i = 0; i < scoreList.Count; i++)
//{
// Console.Write(scoreList[i] + " ");
//}
foreach (var temp in scoreList)
{
Console.Write(temp + " ");
}
Console.ReadKey();
操作列表的屬性和方法
- 主要操作方法
Capacity:獲取容量大小
Add():添加元素
Insert():插入元素
[Index]:訪問元素
Count:訪問元素個(gè)數(shù)
RemoveAt():移除指定位置的元素
IndexOf():取得一個(gè)元素所在列表的索引位置(從前往后搜)
LastIndexOf():從后往前搜
Sort():對(duì)列表中元素進(jìn)行從小到大排序 - 向指定索引位置插入元素,不可以超出元素個(gè)數(shù)
泛型類的定義
- 泛型:
通過參數(shù)化類型來實(shí)現(xiàn)在同一份代碼上,操作多種數(shù)據(jù)類型。利用“參數(shù)化類型”將類型抽象化,從而實(shí)現(xiàn)靈活的復(fù)用 - 泛型類的定義
定義一個(gè)泛型類指的是,定義一個(gè)類,這個(gè)類某些字段的類型是不確定的,這些類型可以在類構(gòu)造的時(shí)候確定下來。舉例:
1)創(chuàng)建一個(gè)類處理int類型和double類型的相加:
class ClassA<T>//T代表一個(gè)數(shù)據(jù)類型,當(dāng)使用classA進(jìn)行構(gòu)造的時(shí)候,需要指定T的類型
{
private T a;
private T b;
public ClassA(T a, T b)
{
this.a = a;
this.b = b;
}
public T GetSum()
{
return a +b;
}
泛型方法
- 定義泛型方法就是定義一個(gè)方法,這個(gè)方法的參數(shù)可以是不確定的,可以當(dāng)調(diào)用這個(gè)方法的時(shí)候,再去確定方法參數(shù)的類型。
可以實(shí)現(xiàn),任意類型組拼成字符串的方法
public static T GetSum<T>(T a, T b){
return a+""+b;
}
GetSum<int>(23,12); GetSum<double>(23.2,12);


