C#
簡介
C#是微軟公司發布的一種由C和C++衍生出來的面向物件的編程語言,它不僅去掉了 C++ 和 Java 語言中的一些復雜特性,還提供了可視化工具,能夠高效地撰寫程式,
C#是由C和C++衍生出來的一種安全的、穩定的、簡單的、優雅的面向物件編程語言,它在繼承C和C++強大功能的同時去掉了一些它們的復雜特性(例如沒有宏以及不允許多重繼承),
C#使得C++程式員可以高效的開發程式,且因可呼叫由 C/C++ 撰寫的本機原生函式,而絕不損失C/C++原有的強大的功能,因為這種繼承關系,C#與C/C++具有極大的相似性,熟悉類似語言的開發者可以很快的轉向C#,
C#關鍵字
C#初次體驗
使用Visual Studio創建第一個專案 列印Hello world!
1.1 創建專案
打開VS->新鍵專案->找到控制臺程式(.NET Framework)->下一步->創建
eg:
1.2 列印Hello world!
如何編譯當前程式?
- 1.C#程式–人能看懂,機器看不懂
- 2.執行程式的確是機器
- 3.需要將C#程式編譯(翻譯)成機器能夠讀懂的語言(二進制)
- 4.這樣程式就可以被機器執行了
- 5.Windows:生成->生成解決方案 Ctrl + Shift + B
如何運行當前程式?
1.Windows:運行而不除錯(Ctrl + F5/F5)
eg:
注釋
- 1.注釋是不會被編譯的,更不會被執行
- 2.注釋的作用:
- 3.解釋說明當前的代碼是什么含義
3.1、強調
- 1.在目前學習代碼階段,保證你寫的每行代碼都要配一行注釋
- 2.解釋說明,你這句代碼是什么含義
3.1、暫時取消某些代碼的執行
??快捷鍵:
???注釋當前選中行的代碼:Ctrl + K + C
???取消注釋當前選中行的代碼:Ctrl + K + U
MSDN
MSDN地址
https://docs.microsoft.com/zh-cn/dotnet/api/system.reflection.fieldinfo?view=netframework-4.7.2
資料型別
資料的量級
- 1024位元組(byte)=1KB
- 1024KB = 1MB
- 1024MB = 1GB
- 1024GB = 1TB
- 1024TB = 1PB
資料型別
bool 0000 0000 false 0000 0001 true
sbyte 有符號的8位整數 000 0000 — 111 1111 (0-127)
- 1.第一位表示符號(+ -)
- 2.特殊0:1000 0000 和 0000 0000
- 3.硬性規定:1000 0000 表示-128
- 4.取值范圍:-128—+127
byte無符號的8位整數
- 1.0000 0000 —— 1111 1111
- 2.取值范圍 0 ——255
short有符號的16位整數(檔案中是Int16)
- 1.000 0000 0000 0000 —— 111 1111 1111 1111
- 2.負32768到正32767
int有符號的32位整數(檔案中是Int32)
最常用的整數型別
一般說整型即int型別
無特殊情況,存盤一個整數都用int
int占4個位元組(面試經常問)
long有符號的64位整數(檔案中是Int64)
浮點數(通常理解就是生活中的小數)
float(單精度浮點數)【檔案中查:Single】
一般情況下,float足夠
double(雙精度浮點數)【檔案中查:Double】
如果需要精度更高一點,用Double
decimal(高精度浮點數)
極少用到,天文數字可能會用到這個
常量、變數
程式運行期間
程式開始到程式結束
變數:在程式運行期間,可以被改變
變數的宣告
資料型別 變數名 = 初值;
int a = 10;
變數可以不賦初值(在特殊情況下必須要賦初值)
不賦初值時,當前變數的值是默認值
int/float 默認值是0
char 默認值’\0’(表示空字符)
常量:在程式運行期間,不能被改變
常量的宣告
const 資料型別 變數名 = 初值;
大寫(潛規則)
const float money = 100.35f;
常數必須要賦初值
浮點型宣告時注意:
float flo = 1.11f;【float數字后面要加f】
double damage = 1.11d【double后面要加d】
decimal damage = 1.223m【decimal后面要加m】
字符型宣告注意:
字符型別(一定要用單引號括起來)
char cha = ‘name’;
常量及變數的命名規則
只能由字母、數字、@和下劃線(_)這些組成
數字不能開頭,? 1a?、3f?、xiaoming1?
@符號,要么不用,要用必須放在首位,??@zhansan?,zhang@san?
不能與系統的關鍵詞同名,? int,long,sbyte?
變數名不能重復
中文變數名語法上是可以的,但極為不推薦
常量及變數的命名規范
全是英文單詞,不要用拼音
駝峰命名法
大駝峰(每個單詞的首字母大寫,其余字母小寫)
MyHeroDamage、HeroAttack
小駝峰:(第一個單詞首字母不大寫,后面每個單詞的首字母大寫,其余字母小寫)
myHeroDamage、heroAttack【目前階段都用小駝峰】
見名知意
yyds(你曉得這是啥?評論區見)?
運算子
資料的量級
賦值運算子 “=”,是一個運算,將后面的結果賦給前面的變數或常量
前面 = 后面;后面的值賦給前面
前面必須是個變數【不能是具體是數值(2,3,’A‘,“123”)】
后面可以是具體的數值,也可以是變數,也可以是常量
算術運算子
+、- 加減法
、/ 乘除法
a / b 運算之后得到一個結果
被除數 ÷ 除數 = 商
除法有一個禁忌:除數不能為0
% 取余(做除法取余數)
5 % 3 : 5除以3,得到的余數,是結果
上面的+、-、、/、%都是二元運算子
++運算子和–運算子
舉例:a++; 等價于 a = a+1;
++、- -是一元運算子
//int showYouAge = age++;//結果是18
//意味著 age++ 得到的結果是 age
//決議:
//第一步:將age的值賦給showYouAge
//第二步:age自增
int showYouAge = ++age;
//意味著 ++age 得到的結果是 age+1
//決議:
//第一步:age自增
//第二步:將age的值賦給showYouAge
//總結:
//age++;++age;
//++符號在前就先自增,后賦值
//++符號在后就先賦值,后自增
練習題:
符合運算子
a+=b;等價于a=a+b;
a=a+4; ==> a+=4;
a-=b;等價于a=a-b;
a=b;等價于a=ab;
a/=b;等價于a=a/b;
a%=b;等價于a=a%b;
輸入與輸出
輸出
Console.WriteLine();
輸出內容,并換行
Console.Write();
輸出內容,不換行
輸入
Console.Read();
從螢屏讀取一個字符,并回傳該字符所對應的整型數字
Console.ReadLine();
從螢屏讀取一串字符,并回傳該字串
字串是一個資料型別
關鍵詞string
表示一串字符
用雙引號括起來
字串相加可以得到兩個字串組合到一起的字串
預編譯執行 region
作用:代碼分成一個區域,方便折疊和展開
區域首部:#region
區域尾部:#endregion
eg:
型別轉換
隱式轉換
將占用位元組小的、取值范圍小的、精度小的,轉換為占用位元組大的、取值范圍大的、精度高
不需要任何的修飾符,會自動轉換
//整型
//1 2 4 8
//sbyte short int long
sbyte englishScore = 100;
//sbyte --> int
int myScore = englishScore;
//int --> long
long classScore = myScore;
//int --> float
float newScore = myScore;
//float --> double
double newClassScore = newScore;
顯式轉換(強制轉換)
將占用位元組大的、取值范圍大的、精度高,轉換為占用位元組小的、取值范圍小的、精度小的
需要強制轉修飾符,會有精度的缺失,甚至資料的錯誤
轉換情況:知道這個數字,在小的資料型別的取值范圍內
//強制轉換
int damage = 10000;
//int --> sbyte
sbyte health = (sbyte)damage;
//輸出結果
Console.WriteLine(health); //16
float mathScore = 90.5f;
//float --> int
int myAllScore = (int)mathScore;
//會把小數點后面的內容全部舍去
Console.WriteLine(myAllScore); //90
int和char之間的型別轉換
//int 和 char之間的型別轉換
int num = 11101;
//int --> char
char letter = (char)num;
//a-97
Console.WriteLine(letter);
評論區請教大神!!!
int和bool之間的型別轉換
不能進行轉換
string與其他型別之間的轉換
舉例:
“false”,“true”
“10”,“3.14”
“A”
轉換方法
System.Convert
System.Convert.ToBoolean()
System.Convert.ToInt32()
System.Convert.ToSingle()
System.Convert.ToDouble()
System.Convert.ToChar()
//資料型別.Parse()
int.Parse()
bool.Parse()
float.Parse()
char.Parse()
其他型別能不能轉換成字串
其他型別的變數.ToString();
關系運算子 & 邏輯運算子
關系運算子:>,<,>=,<=,==,!=
邏輯運算子:邏輯運算是bool與bool之間的運算
&:與運算
true & true : true
true & false : false
false & false : false
總結:一假則假
|:或運算
true | true : true
true | false : true
false | false : false
總結:一真則真
!:非運算
! true : false
! false : true
&&:短路與運算
普通的&與運算,無論第一個條件是真是假,都會繼續判斷第二條件
短路與運算&&,如果判斷第一個條件已經是假,則不會繼續判斷第二個條件
||:短路或運算
普通的|與運算,無論第一個條件是真是假,都會繼續判斷第二條件
短路或運算||,如果判斷第一個條件已經是真,則不會繼續判斷第二個條件
短路&&、||
優點:第一個條件已經得知整個邏輯運算的結果,就不會去判斷第二個條件了
節約了運算量
缺點:如果判斷中帶有運算,如果不進行第二個條件的判斷,那第二個條件中的運算也不能執行
練習:
手動計算下列運算式的值和number的值,并撰寫程式來驗證結果是否正確
- ①number初值為6:number++ * ++number - number-- / --number
- ②number初值為5:number++ > 3 | --number == 0
- ③number初值為2:number-- > 2 && --number == 0
- ④number初值為12:number++ > 10 && --number != 2 && number-- == 0 && ++number != 3
- ⑤number初值為0:number++ > -1 || --number <= 2 && number-- != 10
- ⑥number初值為0:number++ < -1 || (–number >= 2 && number-- != 10)【小括號優先級最高】
分支陳述句
條件運算子
條件運算子(三元運算子)
條件運算式 ? 結果a : 結果b
if的第一種形式
if(條件運算式){
陳述句1;
}
if的第二種形式
if (條件運算式)
{
陳述句1;
}
else
{
陳述句2;
}
if的第三種形式
if (條件運算式1)
{
陳述句1;
}
else if (條件運算式2)
{
陳述句2;
}
else
{
陳述句3;
}
Switch
如果case 冒號后面沒有任何陳述句,可以不加break;
Switch()括號中是可以允許添加浮點型變數的,但不推薦
浮點型是有誤差的
浮點型一般不做等于的判斷
企業面試題:有一個浮點數,判斷該浮點數是不是等于5
?回圈陳述句
while回圈
while (條件運算式)
{
//回圈內容
}
break關鍵詞
跳出本層回圈(通常與if連用)
continue關鍵詞
結束本次回圈(continue后面的代碼不再執行),進入下次回圈,(通常與if連用)
練習:????????????
從鍵盤輸入一個算數運算式,使用if陳述句實作正確的算數運算,并輸出運算結果
例如:
請輸入第一個數字:4
請輸入運算子:+
請輸入第一個數字:2
計算結果為:6
輸入一個生日日期,輸出其星座.
白羊座:3.21-4.19,金牛座:4.20-5.20,雙子座:5.21-6.21,巨蟹座:6.22-7.22
獅子座:7.23-8.22,處女座:8.23-9.22,天秤座:9.23-10.23,天蝎座:10.24-11.22
射手座:11.23-12.21,魔羯座:12.22-1.19,水瓶座:1.20-2.18,雙魚座:2.19-3.20
例如:
請輸入月:12
請輸入日:21
您是射手座
陣列
一維陣列的初始化
動態初始化
資料型別[] 陣列名=new 資料型別[陣列長度];
此時陣列中每一個元素都是為默認值
int的默認值0
float的默認值0
bool的默認值false
char的默認值‘\0’,表示空字符
string的默認值""
資料型別[] 陣列名=new 資料型別[陣列長度]{元素1,元素2…};
資料型別[] 陣列名=new 資料型別[]{元素1,元素2…};
靜態初始化
資料型別[] 陣列名={元素1,元素2…};
陣列的訪問
從0開始計數
array.Length是陣列的長度,只讀的,
訪問陣列時,切記下標所對應的陣列元素是存在的
如果不存在,就會引發陣列越界例外
參考資料型別
資料型別
值型別(存盤在堆疊記憶體)
堆疊記憶體里存的是具體的值
前面學習過的值型別
int、float、bool、char
參考型別(存盤在堆記憶體)
堆疊記憶體里存的是地址(堆記憶體的地址)
目前學習過的參考型別
string(特例)、陣列
遍歷陣列
遍歷陣列:訪問陣列中的每一個元素
int[] array = { 1,8,4,8,7,45,456,789,78,76};
for (int i = 0; i < array.Length; i++)
{
Console.WriteLine(i + ":" + array[i]);
}
二維陣列、結構體、列舉
冒泡排序
思想:
當前陣列元素與后面的數字元素進行對比,如果前大后小,則進行交換
每輪可以確定一個最大值在陣列的末位,幾輪之后即可完成排序
冒泡排序當然也可以從大到小排序,那樣則前小后大進行互動
代碼:
for (int i = 0; i < array.Length; i++)
{
//立個flag,表示判斷程序中是否發生了交換
bool IsjiaoHuan= false;
for (int j = 0; j < array.Length - i - 1; j++)
{
if (array[j] > array[j + 1])
{
//說明交換了
IsjiaoHuan= true;
//交換
int temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
}
}
if(!IsjiaoHuan)
{
//說明已經排好序了,后面的輪次就沒有必要了
break;
}
for (int m = 0; m < array.Length; m++)
{
Console.Write(array[m] + "\t");
}
Console.WriteLine();
}
Console.WriteLine("排序結束");
for (int m = 0; m < array.Length; m++)
{
Console.Write(array[m] + "\t");
}
二維陣列的定義
資料型別[,] 陣列名;
int[,] array;
二維陣列的初始化
動態初始化
資料型別[,] 陣列名 = new 資料型別[第一維長度,第二維長度];
int[,] arr= new int[50,3];
資料型別[,] 陣列名 = new 資料型別[,]{陣列值};
int[,] arr= new int[,]{{1,0,1},{3,0,5}};
靜態初始化
資料型別[,] 陣列名 = {陣列值};
int[,] arr= {{1,0,1},{3,0,5}};
二維陣列的元素訪問
陣列名[第一維下標,第二維下標]
arr[3,2]
謹防陣列越界
二維陣列的長度
總長度(二維陣列的元素個數)
array.Length
第一維的長度
array.GetLength(0)
第二維的長度
array.GetLength(1)
二維陣列的遍歷
for (int i = 0; i < heroData.GetLength(0); i++)
{
for (int j = 0; j < heroData.GetLength(1); j++)
{
Console.Write(heroData[i,j] + "\t");
}
//換行
Console.WriteLine();
}
foreach迭代遍歷
迭代遍歷是只讀的,不能修改
//foreach性能消耗要大一點,所以能用for的盡量用for
foreach (var item in number)
{
Console.WriteLine(item);
//迭代遍歷是只讀的,不能寫入
//item = 1;
}
列舉型別
列舉型別是一個自定義型別
列舉型別是一個值型別
列舉型別的創建
//裝備型別
enum EquipType
{
Helmet = 100,//頭盔
BodyArmor = 200,//防彈衣
Knapsack,//背包
Knife
}
列舉型別變數的定義與使用
//定義一個列舉型別的變數
EquipType myEquip = EquipType.Knapsack;
EquipType yourEquip = EquipType.Knapsack;
//判斷列舉變數
if(myEquip == EquipType.BodyArmor) { }
switch (myEquip)
{
case EquipType.BodyArmor:
break;
case EquipType.Helmet:
break;
case EquipType.Knife:
break;
//case EquipType.
default:
break;
}
//列舉型別和整型之間的轉換
//列舉型別可以強制轉換為整型
int num = (int)myEquip;
Console.WriteLine(num);
//整型可以強制轉換為列舉型別
myEquip = (EquipType)200;
Console.WriteLine(myEquip);
//既然列舉可以用整數去表示
Console.WriteLine(myEquip+2);
結構體型別
結構體型別是自定義型別
結構體型別是值型別
結構體型別的創建
//學生型別
struct Student
{
public string name;
public char sex;
public int age;
}
結構體型別變數宣告及欄位賦值
//定義一個學生變數
Student xiaoming;
//學生結構內變數賦值
xiaoming.name = "xiaoming";
xiaoming.age = 16;
xiaoming.sex = 'M';
結構體的建構式
結構體默認的建構式,開發者不能創建默認構造(即無參構造)
public Student()
{
}
結構體的自定義建構式,方便創建結構體變數時給欄位賦值
//自定義建構式
public Student(string n,char s,int a)
{
//作用:快速給結構體欄位賦初值
//而且必須給每一個欄位都賦初值
name = n;
sex = s;
age = a;
}
初始化結構體變數
//有了自定義的建構式后,如何新建結構體變數
Student xiaogang = new Student("xiaogang",'M',18);
結構體練習題
eg:
#region 結構體、列舉
//創建英雄裝備結構體,包含名稱,攻擊力加成,法術強度加成,血量加成,裝備型別
enum EquipType
{
AD,
AP,
HP,
Other
}
//英雄裝備
struct HeroEquip
{
public string name;
public float adAddition;
public float apAddition;
public float hpAddition;
public EquipType equipType;
public HeroEquip(string name, float adBuff, float apBuff, float hpBuff, EquipType equipType)
{
//給所有欄位賦初值
this.name = name;
adAddition = adBuff;
apAddition = apBuff;
hpAddition = hpBuff;
this.equipType = equipType;
}
}
#endregion
#region 結構體、列舉練習
//有5個裝備保存在結構體陣列當中,編程找出血量加成最高者
//對裝備陣列按照攻擊力加成排序并使裝備按照攻擊力加成升序進行資訊列印
HeroEquip wjzr = new HeroEquip(
"無盡之刃", 100, 0, 50, EquipType.AD);
HeroEquip yxj = new HeroEquip(
"飲血劍", 80, 0, 20, EquipType.AD);
HeroEquip ktkj = new HeroEquip(
"狂徒鎧甲", 0, 0, 150, EquipType.AD);
HeroEquip dmj = new HeroEquip(
"蘭德里的折磨", 20, 100, 0, EquipType.AD);
//宣告結構體陣列存盤這些裝備
HeroEquip[] heroEquips = { wjzr, yxj, ktkj, dmj };
//設定初始血量最大值
float maxHPBuff = heroEquips[0].hpAddition;
//設定初始血量最大值的裝備名稱
string maxHPEquipName = heroEquips[0].name;
HeroEquip maxHPEquip = heroEquips[0];
//找血量最大
for (int i = 0; i<heroEquips.Length; i++)
{
if (maxHPBuff<heroEquips[i].hpAddition)
{
//更新最大值
maxHPBuff = heroEquips[i].hpAddition;
//更新最大值的裝備名稱
maxHPEquipName = heroEquips[i].name;
}
? //如果宣告結構體
? if (maxHPEquip.hpAddition<heroEquips[i].hpAddition)
? {
? maxHPEquip = heroEquips[i];
? }
}
Console.WriteLine("裝備串列中,血量加成最高的裝備是\n"
maxHPEquip.name + ",最大值是"
xHPEquip.hpAddition);
Console.Read();
#endregion
面向物件
面向程序
重點關心解決問題的步驟
優點:
可以很清晰的看明白問題解決的步驟
代碼的行數要少一些,性能消耗低一些
缺點:
不易維護、不易拓展、不易復用
面向物件
重點關心解決問題程序中參與的物件有哪些,分別有哪些特性和行為
優點:
易維護、易拓展、易復用
缺點:
代碼是分散的,行數會多一些
性能消耗要高一些
類和物件
創建一個類
[訪問修飾符] Class 類名//大駝峰命名法
創建一個類的物件
類名 物件名;
類的型別是一個參考類的型
類的型別是一個自定義型別
一個物件在創建后,需要進行實體化(初始化)才能使用
類名 物件名 = new 類名();
原理 : 物件在進行new操作后,才分配了記憶體,
欄位
描述類的特性
要使用小駝峰命名法命名
可以有初值
幫助注釋
幫助注釋也是一種注釋
誰可以添加幫助注釋
類
方法
欄位
屬性
幫助注釋的優點
在使用類、方法、欄位、屬性的時候,可以在提示框中顯示幫助注釋的內容
this關鍵詞
表示當前物件
如果沒有重名沖突,可以不寫(省去)
Random亂數類
第一步,先創建一個亂數物件
Random random = new Random();
第二步,呼叫亂數物件的Next方法
呼叫時要傳入兩個引數,即亂數字的取值范圍
-
如傳入的是4和8,則亂數字范圍為4至7,不包含8(這個方法就是這么寫的)
-
即取值范圍為左閉右開,即[min,max)
-
方法回傳的結果即隨機到的數字,需要用整型變數去接收
int num = random.Next(0,array.Length);
1
方法
方法是描述一個類的某個特定行為
一個方法盡量就完成一件小的事情
如果要完成一件大事
先定義一些方法,完成這個大事拆分出來的小事
最后在按照流程,在大事方法中呼叫這些小事的方法
方法要用大駝峰命名法命名
方法的創建[訪問修飾符] 回傳值型別 方法名(引數串列) { //方法體 //實作方法功能 return 結果;//最終別忘了回傳方法結果,結果型別需與回傳值型別保持一致 }
方法的呼叫
物件名.方法名(引數串列);//無論有無引數,小括號都要存在
return關鍵詞
回傳結果
終止方法,即return后面的陳述句不會執行
屬性、方法引數
屬性
屬性命名方式使用大駝峰
在屬性訪問器內寫代碼,切記一點
Get訪問器內不要寫讀取該屬性的陳述句
Set訪問器內不要寫寫入該屬性的陳述句
否則,會出現遞回回圈,死回圈
屬性簡寫:public string Name { get; set; } = “先生”;
參考引數ref
添加了ref關鍵詞的引數
傳遞的就不是值了
而是地址
而如果沒有賦初值,是沒有地址的
所以ref引數一定是個變數
所以ref引數的實參一定是賦過初值
所以ref一般加在值型別引數的前面
使用應用引數,無論是形參還是實參前面都要加ref關鍵詞
輸出引數out
添加了out關鍵詞的引數
引數就成了一個輸出的通道
離開方法之前形參必須賦值
實參必須是一個變數
傳遞的實參一般是值型別
使用輸出引數,無論是形參還是實參前面都要加out關鍵詞
字串
StringBuilder、多載、遞回
StringBuilder
在System.Text命名空間下
所以使用時,需要先引入這個命名空間
using System.Text;
如果不引入,可以直接寫:
System.Text.StringBuilder strB = new System.Text.StringBuilder();
使用時,先要new
StringBuilder stringB = new StringBuilder();
追加字串
//追加字串
stringBuilder.Append("天氣好晴朗");
什么時候用StringBuilder而不用string
字串需要經常修改的時候
方法的多載
同一個類里面,方法名一樣,但引數不一樣
引數的資料型別不一樣
引數的個數不一樣
引數的資料型別不一樣,個數也不一樣
引數個數和型別都一樣,但回傳值型別不一樣,不能算做多載?
class Funs
{
public string Fun(string a, string b)
{
return "a+b";
}
public int Fun(int a, int b)
{
return a + b;
}
}
Funs funs = new Funs();
int a = funs.Fun(1, 1);
string b = funs.Fun("2", "2");
string c = string.Format("{0},{1}", a, b);
Console.WriteLine(c);
Console.Read();
遞回
方法自己呼叫自己
多個方法之間來回呼叫
使用遞回時一定要有出口
使用遞回時,一定概要慎重慎重再慎重…
建構式
執行的時機
new物件()的時候呼叫
建構式一般都是public
因為一旦建構式被設定為了private,那么外界就無法new這個
建構式沒有回傳值
也不能寫回傳值型別
建構式的名字必須和類名保持完全的一致
建構式是不能像普通方法一樣被直接呼叫的
關于系統會不會自動創建一個空參空方法體的構造給你
如果一個類沒有建構式
那么系統會自動寫一個空引數空方法體的建構式
如果有建構式
那么系統就不會幫你自動創建
建構式可以有參、也可以無參
建構式是可以多載的
如果想在執行當前建構式之前,先執行其他的建構式
當前建構式(…) : this(傳實參)
class Person
{
private int age;
private string name;
public Person(int age)
{
this.age = age;
Console.WriteLine(age);
}
public Person(string name) : this(18)
{
this.name = name;
Console.WriteLine(name);
}
public Person(int age, string name) : this("xian")
{
Console.WriteLine("我是性別,年齡!");
}
}
static void Main(string[] args)
{
Person body = new Person(128, "alll");
Console.Read();
}
多型
關于父類空引數建構式的呼叫說明
首先,先要明確一點
子類在實體化的時候,必會呼叫父類的建構式
子類在宣告建構式的時候,要想辦法呼叫父類的構造
如果父類是空引數的建構式 : base()
可以不寫:base()
系統會默認呼叫父類的空引數構造
如果父類是有引數的建構式,那么一定概要通過:base的方式呼叫,傳參
關于VS自動生成類建構式
右鍵類名
點擊快速修復或重構
點擊生成構造
public class Person
{
public string name;
public Person(string name)
{
this.name = name;
Console.WriteLine(name);
}
public void Say()
{
Console.WriteLine("你在干什么!");
}
}
public class Person1:Person
{
public Person1(string name):base(name)
{
}
public new void Say()
{
Console.WriteLine("弄啥呢!");
}
}
public class Person2:Person1
{
public Person2(string name) : base(name)
{
}
public new void Say()
{
Console.WriteLine("搞啥呢!");
}
}
static void Main(string[] args)
{
Person p = new Person("1");
p.Say();
Person1 p1 = new Person1("2");
p1.Say();
Person2 p2 = new Person2("3");
p2.Say();
PersonFun(p2);
Console.ReadLine();
}
public static void PersonFun(Person person)
{
person.Say();
}
子類方法的覆寫
前提條件:父類中有一個public函式,子類中沒有該函式
因為子類中并沒有該函式,所以呼叫必是父類的
前提條件:子類里已經有了該函式,父類里面也有該函式
此時,子類物件呼叫子類的該函式,父類物件呼叫父類的該函式
這種子類函式,可以稱之為覆寫
子類在書寫該函式的時候,規范的寫法應該是:
[訪問修飾符] new 回傳值型別 函式名(引數串列)
覆寫:子類也有該函式了,以后呼叫的時候就呼叫子類的該函式
子類方法的重寫【表現出多型】
如果父類想要子類可以重寫該函式
那么父類的該函式必須是一個虛函式
[訪問修飾符] virtual 回傳值型別 函式名(引數串列)
子類該怎么重寫
[訪問修飾符] override 回傳值型別 函式名(引數串列)
重寫:把子類和父類的該函式都重新寫了一遍,有的新的內容
此時,子類的物件,無論是不是轉換成了父類的型別,都會執行重寫后的該函式
關于VS自動生成類重寫函式
右鍵類名
點擊快速修復或重構
點擊生成重寫
public class Person
{
public string name;
public Person()
{
}
public Person(string name)
{
this.name = name;
}
public virtual void Say()
{
Console.WriteLine("我是父類的方法");
}
}
public class Person1 : Person
{
public Person1(string name)
{
}
public override void Say()
{
Console.WriteLine("我是字類的方法");
}
}
static void Main(string[] args)
{
Person1 p1 = new Person1("我是字類");
p1.Say();
Person p = new Person1("我是父類");
p.Say();
Console.ReadLine();
}
所有類的最侄訓類:Object
所以,所有類都可以重寫Object類中的虛方法
Object的虛方法有三個:
Equals:描述物件與物件之間是否相等
GetHashCode:將一個物件編程一串數字
ToString:將一個物件轉換為一個字串
抽象方法
抽象方法的關鍵詞abstruct
abstruct 回傳值型別(引數串列)
抽象方法必須要放在抽象類里面
抽象方法沒有方法體: [訪問修飾符] abstruct 回傳值型別 方法名(形參串列);
抽象方法的訪問修飾符不能是private
抽象類即可以放抽象方法,也可以放普通方法
抽象方法必須在子類中全部實作
除非子類也是一個抽象類,那么可以先不實作該抽象方法
抽象方法和虛方法最大的區別
抽象方法必須其派生類中得以實作
而虛方法不是一定要在其派生類去重寫
無論是虛方法還是抽象方法,子類進行了重寫【實作】
那么子類的子類依舊可以繼續重寫
抽象類不能用sealed關鍵詞修飾
總結:override可以重寫哪些方法呢?
帶有virtual、abstruct、override關鍵詞的方法
代碼:
//抽象類
public abstract class Person
{
public void Fun(string name)
{
Console.WriteLine(name);
}
public abstract void Say();
}
public class Person1 : Person
{
public override void Say()
{
Console.WriteLine("asd");
}
}
static void Main(string[] args)
{
Person1 a = new Person1();
a.Say();
Console.Read();
}
虛方法和抽象方法的區別:
- (1)抽象方法是只有方法名稱,沒有方法體,即沒有方法的具體實作,子類必須重寫父類抽象方法才能實作具體功能;虛函式有方法名稱也也有方法體,但是子類可以覆寫,也可不覆寫,
- (2)抽象方法是一種強制派生類覆寫的方法,否則派生類將不能被實體化,
- (3)抽象方法只能在抽象類中宣告,虛方法不是,
- (4)派生類必須重寫抽象類中的抽象方法,虛方法則不必要,
虛方法:
- 1、virtual方法表示此方法可以被重寫, 也就是說這個方法具有多型.父類中的方法是通用方法,可以在子類中重寫以重新規定方法邏輯,
- 2、virtual方法可以直接使用,和普通方法一樣,
- 3、不是必須重寫的. 子類可以使用base.方法 的方式呼叫, 無論有沒有在子類使用override去重寫,
(virtual關鍵字只是明確標示此方法可以被重寫, 其實它和一般的方法沒有什么區別
【sealed關鍵字標示此方法不可以被重寫】)
抽象方法:
- 1、抽象方法沒有提供實作部分,
- 2、抽象方法只能在抽象類中宣告,
- 3、抽象方法是一種強制派生類覆寫的方法,否則派生類將不能被實體化,
sealed關鍵詞
密封類
sealed關鍵詞修飾的類稱之為密封類
語法:sealed class 類名
密封類是不能被別的類繼承的
密封方法
sealed關鍵詞修飾的重寫的方法,稱之為密封方法
語法:sealed override 回傳值型別 方法名(引數串列)
密封方法無法再次被其子類重寫
代碼:
public class Person
{
public virtual void Fun()
{
Console.WriteLine(1);
}
}
public class Person1 : Person
{
public sealed override void Fun()
{
Console.WriteLine(2);
}
}
public class Person2 : Person1
{
//這里報錯 因為續承的Person1是密封函式
public override void Fun()
{
base.Fun();
}
}
static void Main(string[] args)
{
Person a = new Person1();
a.Fun();
Console.Read();
}
靜態類
關鍵詞 static
靜態成員
成員:欄位、屬性、方法
靜態:跟物件沒有任何關系,只跟類有關系
靜態成員在何時開辟的記憶體
第一次訪問這個類的時候【第一次用到這個類的時候】
比如:用這個類名去實體化一個物件
比如:用這個型別去訪問一個靜態欄位
靜態成員在何時釋放記憶體?
在程式結束的時候才會釋放
普通的實體成員,每有一個物件,就有一個該成員
而靜態成員,跟物件沒有關系,所以無論有多少個物件,靜態成員都只有一個
例: 實體成員【name】,每有一個人,就會有對應的一個名字
而靜態成員【Population】,跟物件沒有關系,無論有多少個實體物件,人口數量只有一個
靜態方法中是不可以訪問非靜態的成員的
不能訪問非靜態的欄位、屬性
不能呼叫非靜態的方法
非靜態方法中是可以訪問靜態成員的
能訪問靜態的欄位、屬性
能呼叫靜態的方法
靜態方法是可以有多載
靜態類
靜態的成員可以放在靜態類中,也可以放在非靜態類中
靜態類中只能存在靜態成員,不能存在非靜態的成員
靜態類是不能進行實體化的
靜態建構式
只有一種寫法
static 類名()
靜態建構式必須無引數
靜態建構式在什么時候才會呼叫
靜態建構式在程式運行期間只會執行一次
在第一次訪問該類的時候呼叫
用這個類去new一個物件
用這個類去訪問某個靜態成員
用這個類去呼叫某個靜態方法
如果有繼承關系
靜態建構式的執行順序是:
先執行子類的靜態構造,再執行父類的靜態構造
先子后父
靜態構造有什么作用?
一般用于對靜態成員進行初始化
代碼
class Person
{
public static float age=88;
public static void Fun()
{
Console.WriteLine("我是父靜態類!");
}
static Person()
{
Console.WriteLine("我是基靜態類!");
}
}
class Per : Person
{
static Per()
{
Console.WriteLine("我是子靜態類!");
}
}
static void Main(string[] args)
{
Per p = new Per();
Console.WriteLine();
Console.ReadLine();
}
集合、堆疊、堆、佇列、字典
1、集合~范型(命名空間using System.Collections.Generic;)
1.1、ArrayList
代碼:
//實體化動態陣列
ArrayList score = new ArrayList();
//向動態陣列中添加元素
score.Add(90);
score.Add(85.5f);
score.Add("English:100");
int[] array = { 90,80,70 };
//向動態陣列中批量添加元素
score.AddRange(array);
//向動態陣列中插入元素
score.Insert(2, "Math:80.5");
//洗掉動態陣列中的元素
score.Remove(85.5f);
//洗掉的是單個約束,如果動態陣列中沒有該元素,就忽略
score.Remove(90);
//根據下標移除動態陣列中的元素
score.RemoveAt(0);
score.AddRange(new string[] { "A", "B", "C", "A" });
//批量洗掉元素
score.RemoveRange(2, 3);
//如果陣列中沒有該下標所對應的元素,則也會出現越界例外
Console.WriteLine(score[1]);
//陣列元素翻轉
score.Reverse();
//一般想要洗掉某個元素,會先進行判斷是否存在
bool containsA = score.Contains("A");
Console.WriteLine("containsA:" + containsA);
//判斷陣列中是否包含某個元素
if(score.Contains("A"))
{
score.Remove("A");
}
Console.WriteLine("Count:" + score.Count);
//給一個元素的值,查該值在動態陣列中的下標
Console.WriteLine("IndexOf:" + score.IndexOf(790));
score.Clear();
score.AddRange(new float[] { 1.1f,5.7f,4.5f,9.8f,3,2,1});
//從小到大排序
score.Sort();
//清空動態陣列
//score.Clear();
Console.WriteLine("-------------");
for (int i = 0; i < score.Count; i++)
{
Console.WriteLine(score[i]);
}
Console.WriteLine("-------------");
foreach (var item in score)
{
Console.WriteLine(item);
}
1.2、List
代碼:
//初始化范型集合
List<int> list = new List<int>();
//添加元素
list.Add(200);
list.Add(250);
list.Add(280);
//批量添加元素
list.AddRange(new int[] { 1,3,5,6,7,9 });
//移除某個元素
list.Remove(280);
//通過下標移除某個元素
list.RemoveAt(0);
list.RemoveAt(0);
//批量洗掉
list.RemoveRange(0, 3);
int index = list.IndexOf(9);
Console.WriteLine(index);
Console.WriteLine("-----------");
foreach (var item in list)
{
Console.WriteLine(item);
}
2、堆疊(Stack )
說明:后進先出
代碼:
eg:非范型為例??
Object<=Stack stack = new Stack();
//進堆疊
stack.Push("我是第一個進去的");
stack.Push("我是第二個進去的");
stack.Push("我是第三個進去的");
//出堆疊
Console.WriteLine(stack.Pop());
Console.WriteLine(stack.Pop());
Console.WriteLine(stack.Pop());
Console.Read();
//回傳堆疊頂的元素Peek();
常用方法
Stack.TryPeek(T) 方法
回傳一個值,該值指示 Stack 的頂部是否有物件;如果有,則將其復制到 result 引數, 不從 Stack 中洗掉物件,
public bool TryPeek (out T result);
回傳
Boolean
如果 Stack 的頂部有物件,則為 true;如果 Stack 為空,則為 false,
Stack.TryPop(T) 方法
public bool TryPop (out T result);
回傳一個值,該值指示 Stack 的頂部是否有物件;如果有,則將其復制到 result 引數,并從 Stack 中洗掉它,
回傳
Boolean
如果 Stack 的頂部有物件,則為 true;如果 Stack 為空,則為 false,
3、堆(heaps)
4、佇列(Queue)
說明:先進先出
eg:進列
eg:出列
代碼:
eg:非范型為例??
Queue queue = new Queue();
//進列
queue.Enqueue("我是第一個進去的");
queue.Enqueue("我是第二個進去的");
queue.Enqueue("我是第三個進去的");
//出列
Console.WriteLine(queue.Dequeue());
Console.WriteLine(queue.Dequeue());
Console.WriteLine(queue.Dequeue());
Console.Read();
常用方法:
5、字典(Dictionary<string,int>)
代碼:
Dictionary<string, int> dis = new Dictionary<string, int>();
dis.Add("我是第一個進去的", 1);
dis.Add("我是第二個進去的", 2);
dis.Add("我是第三個進去的", 3);
Console.WriteLine(dis.Values);
Console.WriteLine(dis.ContainsValue(2));
Console.WriteLine(dis.ContainsValue(3));
Console.Read();
6、常見的介面
單例、介面和范型
1、單例
如果一個物件在宣告時直接實體化【new】,
在訪問這個類的時候呼叫
實體化的時間點最早,在靜態構造之前就執行了
2、介面
介面相比類,最大的不同之處在于,只有定義沒有實作
介面相當于一堆骨架,實作介面的類,用于填充骨架上的肉
介面不能進行實體化,只能被類或其他介面實作
如即繼承類,又實作介面時,類要放在最前面,介面放在后面
eg:
class Person1:Person,jiankou1,jiekou2
2.1、介面和抽象類的對比
相同點
兩者都不能被實體化
兩者都包含了由其他類或結構繼承或實作的抽象成員不同點
不同點
抽象類當中除了擁有抽象成員外還可以擁有非抽象成員;而介面中所有的所有成員都是抽象的
抽象成員可以使用修飾符修飾,介面當中介面成員訪問級別是默認不可修改的,并且默認是public
介面當中不可以包含構造方法,析構方法,靜態成員以及常量
C#類只支持單繼承,介面支持多繼承
3、范型
范型類的建構式不用寫范型類
有些時候多載的方法只有引數型別不同,其他的都一樣,這時就可以使用泛型,
泛型:需要用戶自己傳過來的一個資料型別
平時方法里傳遞的是引數,是變數,引數傳遞用的是小括號(),
而泛型傳遞的是資料型別,泛型傳遞用的尖括號<>
泛型定義之后,一定要用,不然就沒有意義
泛型都在方法的哪里用?
定義引數
在方法體呢使用泛型定義區域變數口
設定回傳值型別是一個泛型,給泛型添加約束
給范型舔加約束
方法名(引數串列)where 泛型:約束內容
方法名(引數串列)where 泛型A:約束內容1,約束內容2 where 泛型B:約束內容3
關于泛型方法的多載
如果泛型的個數不同,可以多載
如果泛型的個數相同,但約束不同,不可以多載
關于泛型類和泛型介面
class 類名<T,F>
類中的欄位型別、方法引數、方法回傳值,都可以使用類中的泛型
interface 介面名< T >
代碼:
-
public class fanxing { } public static void FX<T>(T sex, T age) { T temp = sex; sex = age; age = temp; Console.WriteLine(age); } public static void FX<T>(T sex, T age, T a) { T temp = sex; sex = age; age = temp; Console.WriteLine(age); } static void Main(string[] args) { FX<int>(7,8); FX<float>(7,8); Console.ReadLine(); } -
范型方法的范型多載
eg 代碼:
public static void faning<T>()
{
}
public static void faning<T,F>()
{
}
public static void faning<T,F,U>()
{
}
static void Main(string[] args)
{
faning<>
}
委托與事件
1、委托(delegate)
2.1常見委托型別
什么是委托:相當于中介
委托的別名:代理、句柄
委托是自定義型別
委托是參考型別
代碼eg:
eg:非范型為例
//定義委托
delegate void weituo(float momey);
class p1
{
public void zhao1(float momey)
{
Console.WriteLine("中介一需要:"+momey+"$");
}
public void zhao2(float momey)
{
Console.WriteLine("中介二需要:"+ momey + "$");
}
public void zhao3(float momey)
{
Console.WriteLine("中介三需要:"+ momey + "$");
}
public void zhao4(float momey)
{
Console.WriteLine("中介四需要:"+momey + "$");
}
}
internal class Program
{
static void Main(string[] args)
{
p1 p = new p1();
? weituo we;
? we = p.zhao1;
? we += p.zhao2;
? we += p.zhao3;
? we += p.zhao4;
? we(1232143);
? Console.Read();
?
? }
}
2.2系統委托型別
無回傳值系統委托 Action<>
代碼eg:
eg:
class p1
{
public void wcs()
{
Console.WriteLine("我是無引數的中介需要:" + "18456456456456"+ "$");
}
public void zhao1(float momey)
{
Console.WriteLine("中介一需要:"+momey+"$");
}
public void zhao2(float momey)
{
Console.WriteLine("中介二需要:"+ momey + "$");
}
public void zhao3(float momey)
{
Console.WriteLine("中介三需要:"+ momey + "$");
}
public void zhao4(float momey)
{
Console.WriteLine("中介四需要:"+momey + "$");
}
}
internal class Program
{
static void Main(string[] args)
{
p1 p = new p1();
//使用系統委托
//無引數
Action action;
action = p.wcs;
action();
? //引數
? Action<float> action1;
? action1 = p.zhao1;
? action1 += p.zhao2;
? action1 += p.zhao3;
? action1 += p.zhao4;
? action1(182);
? Console.Read();
? }
}
有回傳值系統委托 Func<>
代碼eg:
eg:
class Hout
{
}
class p1
{
public string Func1()
{
return "有回傳值函式無引數:";
}
? public Hout Func2(string name)
? {
? Console.WriteLine("有回傳值函式有引數:"+name);
? return null;
? }
}
internal class Program
{
static void Main(string[] args)
{
p1 p = new p1();
? Func<string> func;
? func = p.Func1;
? Console.WriteLine(func());
? Func<string,Hout> func1;
? func1 = p.Func2;
? Hout a= func1("先生");
? Console.Read();
? }
}
2.3委托之匿名函式
代碼eg:
eg:
class p1
{
public Action<string> action;
public string name;
public int age;
? public p1(string name, int age)
? {
? this.name = name;
? this.age = age;
? }
? public void isAction(string age)
? {
? Console.WriteLine("我是個中介:"+ age);
? if (action != null)
? {
? action(age);
? Console.WriteLine("我是個中介:" + age);
? }
? }
}
internal class Program
{
static void Main(string[] args)
{
p1 p = new p1("先生",18);
? p.action = delegate (string a)
? {
? Console.WriteLine(a);
? Console.WriteLine("我好蒙啊2");
? };
? p.action += read;
? p.isAction("我好蒙啊1");
? Console.Read();
? }
? public static void read(string name)
? {
? Console.WriteLine(name);
? Console.BackgroundColor = ConsoleColor.Red;
? Console.ForegroundColor = ConsoleColor.Black;
? }
}
???如何使用VS進行程式除錯
程式是從Main函式開始,順序執行的
除錯就是在你覺得可能會發生問題的地方打下斷點
什么是斷點?
讓程式執行到這個地方暫停下來的點
有什么好處,可以一句句或一段段讓程式執行
除錯步驟
給想要暫停的代碼行添加斷點
開始除錯
通過監視或區域變數視窗,去看此時你想觀察的變數的值
如果想看變數or物件的記憶體地址
找到即時視窗
&變數名
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/539201.html
標籤:.NET技术
上一篇:C#基礎總結
下一篇:正確使用 HttpClient