閑着沒事就把以前學習時的筆記拿出來整理了一下,個人感覺有點用,就想拿出來跟園友共享一下。有些基礎性的內容比如基本概念、語法什么的就不發了。
內容:1、構造方法(函數) 2、繼承 3、訪問修飾符 4、靜態和非靜態 5、隱藏基類方法 6、重寫基類方法 7、抽象方法 8、接口 9、多態和接口 10、值類型與引用類型 11、ref和out 12、類型轉換 13、異常處理 14、string字符串處理 15、string常用方法 16、StringBulider 17、File類讀取文件 18、文本高亮 19、泛型集合List<T>和Dictionary<TKey,TValue> 20、裝箱和拆箱 21、讀寫數據(Ststem.IO) 22、文件流(FileStream) 23、StreamReader和StringWriter 24、File和Directory 25、序列化 26、Directory類的靜態方法 27、正則表達式 28、Regex類 29、字符串提取 30、貪婪模式 31、字符串替換 32、byte數組和字符串的轉換 33、委托 34、多播委托 35、事件 36、委托和事件的區別 37、var類型 38、擴展方法 39、XML
1、構造方法(函數):在對象創建的時候初始化字段
public 類名([參數])
{
//執行內容,如this.name=name;(有參數)
}
構造方法沒有返回值,void也不寫
創建對象時如果沒寫構造方法系統默認有一個無參的構造方法,一旦用戶添加了構造方法,原來默認的無參的構造方法就沒有了
默認初始值為:int:0; bool:false; string:null; char:‘\0’;
構造方法的作用:初始化字段(實例化類的時候傳入初始值)
調用構造方法:
1)除了new對象的時候,不能直接使用構造方法名調用構造方法
2)this調用構造方法(難點)(一個構造方法調用另一個構造方法)
為了解決代碼的重用才使用this調用構造方法
當一個類中出現多個構造方法重載的時候,同時構造方法都需要為字段賦初值
使用this代表當前類的構造方法來使用,根據this后面的參數決定使用哪一個重載
public 構造方法名():this([參數])
{
//代碼
}
如:
public Person(string name, int age, string sex)
{
this.name = name;
this.age = age;
this.sex = sex;
}
public Person(string name)
: this(name, 0, null)
{
}
2、繼承:
多個類都有共同的字段、屬性或方法時,可以使用繼承
需要寫一個學生類、一個老師類、一個校長類
事先寫一個Person類,在寫其他類的時候繼承這個類,Person叫父類,繼承他的叫子類
特征:子類既有父類的非私有字段、屬性和方法,又有子類獨有的字段、屬性和方法
一個子類只能有一個父類,一個父類可以有若干個子類
[訪問修飾符] class 子類名:父類名
子類調用父類構造方法的問題(難點)
->構造方法的執行順序:先父類再子類
->關於構造方法的重載:如果不聲明調用哪個構造方法默認執行無參的構造方法
避免錯誤的方法:
->為父類提供無參構造方法
->在子類中指定調用父類有參的構造方法
注意:父類除構造函數(方法)之外的非私有成員都可以被子類所繼承,構造方法不可以被繼承,但可以被調用,調用方法: [訪問修飾符] 構造方法名([參數]):base(父類構造方法的參數)
public Teacher():base("狗蛋”,20,"女")
{ } //直接賦值,實例化對象時不需再賦值
public Teacher(string name, int age, string sex)
: base(name, age, sex)
{
this.className = "C#";//老師類獨有的字段
} //繼承傳遞參數,實例化對象時需要賦值
base關鍵字用於顯示聲明要調用父類的哪個構造方法,是調用,不是聲明不是繼承
注意:為避免繼承父類時因為構造方法出錯,應該為每個聲明的類都手動添加一個無參的構造方法
3、訪問修飾符
public 最公開的,所有地方都可以訪問
private 最隱蔽的,只有本類可以訪問
protected 可以在當前類和子類中被訪問
internal 只能在本項目中被訪問
C#中規定類的訪問修飾符只能是public 和internal
類中成員的訪問修飾符除以上四種以外還有個(protected internal)即可以在當前類、子類和本項目中被訪問
4、靜態和非靜態
靜態使用static標記
靜態成員
->如何定義靜態成員
在成員前加static關鍵字
->如何使用靜態成員
靜態成員不能由實例對象調用,只能用類名調用
使用靜態成員,不需要實例化對象
靜態成員會在整個應用程序退出時才釋放資源,所以這個變量可以在整個程序中共享數據,可用於窗體間傳遞參數
注意:靜態變量過多會占用系統內存,因此聲明靜態變量時要多加考慮
Main方法是靜態的,所以它只能調用靜態的方法或參數,若想調用非靜態方法,必須new一個類的實例,用實例名調用。
靜態類
1)什么情況下要將一個類標記為靜態類?
一般情況是,當這個類是一個工具類,里面都是方法,為了讓用戶調用的時候方便,不需要實例化對象,這時可以將該類標記為static類,此時該類中只能包含靜態成員,不能包含實例成員,比如Convert、Console、Read、ReadInt等
2)什么情況下需要在一個普通類中編寫一個靜態成員,而這個類不能標記為static?
當這個類需要被實例化的時候,如果這個類中有一個成員是所有對象要共享的數據,這時可以將該類中的這個成員標記為靜態的,但是這個類還是一個非靜態類
3)靜態類不能被實例化,不能被繼承
4)由於靜態成員會在整個程序退出時才釋放資源,所以盡量避免寫靜態字段或靜態屬性,最好只寫靜態方法
5)靜態類中可以有靜態成員和實例成員,因為其不能被實例化,所以靜態類中的實例成員無法被調用,也就沒有任何意義了
6)靜態類不能被繼承,也不能繼承自其它類,只能繼承自Object類
靜態構造函數
1)靜態構造方法(或字段)在第一次訪問這個類的時候執行,並且只執行一次
->靜態構造方法與靜態類的生命周期(了解)
靜態成員屬於該類的所有對象。實例成員只屬於當前實例
靜態類的生命周期:第一次訪問類的時候創建,程序結束時才釋放
5、隱藏基(父)類方法(了解,用的不多)
當子類和父類有相同名字的方法時,實例化子類調用該方法為子類內的方法,把子類轉換成父類后再調用該方法就是父類內的方法了
為了代碼規范化,如要有意隱藏父類的方法,要在子類的同名方法前加new關鍵字,用於 提醒這里是要隱藏父類方法
//隱藏基類方法
class MyBase
{
public void Func()
{Console.WriteLine("我是父類的方法);}
}
class MySub : MyBase
{
public new void Func() //在方法前用new關鍵字提醒這里是有意隱藏父類方法
{Console.WriteLine("我是子類的方法);}
}
class Program
{
static void Main(string[] args)
{
MySub ms = new MySub();
ms.Func();
// ((MyBase)ms).Func();
MyBase my = ms;
my.Func();
Console.ReadKey();
}
}
6、重寫基(父)類方法(多態的體現)
在父類方法前加virtual(虛方法),表示這個方法可以被重寫
在子類方法前加override,表示重寫基類方法,子類如果不寫override就是隱藏
當子類和父類有相同名字的方法時,實例化父類調用的是父類的方法,將子類賦值給父類后,父類調用的就是子類的方法了
一旦父類的方法在子類中被重寫,除了實例化父類調用以外,子類或者子類的子類調用時都是調用的最新的那個子類重寫方法
此時base和this就有區別了:
如果子類和父類中有名字相同的方法,那么base.Func()就代表父類的方法,this.Func()代表當前類的方法
如果不希望子類再重寫,需要在子類方法前加sealed
//重寫基類方法
class USB
{
public virtual void Usb() //virtual關鍵字表示父類方法可以被重寫′
{Console.WriteLine("我是父類的方法);}
}
class Phone:USB
{
public override void Usb()//override關鍵字表示這個方法重寫父類的方法
{Console.WriteLine("手機");}
}
class Mp3:USB
{
public override void Usb()
{Console.WriteLine("MP3");}
}
class Program:USB
{
static void Main(string[] args)
{
Console.WriteLine("輸入數字");
USB usb = new USB();
switch (Console.ReadLine() )
{
case "1":
usb = new Phone();
break;
case "2":
usb = new Mp3();
break;
default:
break;
}
usb.Usb();
Console.ReadKey();
}
}
隱藏看類型,重寫只管新
7、抽象方法
方法前加abstract,圓括號后加分號
[public] abstract void 方法名(參數);
抽象方法所在的類也必須是抽象的,不能被實例化,類前加abstract
abstract class 類名
抽象成員必須出現在抽象類中,但抽象類中可以有抽象成員和實例成員
抽象類的抽象方法默認可以且必須被重寫,所以需要重寫時不需要再寫virtual,直接在子類方法中寫override關鍵字即可重用
抽象類的成員:方法、屬性、索引、事件
注意:抽象類中的所有抽象方法在子類中必須要被重寫,如果子類中不重寫,那這個子類也必須聲明成抽象類,但此時子類也就不能被實例化了
8、接口
[訪問修飾符] interface 接口名(接口一般以I開頭,I+行為+able)
{
//接口成員,一定是抽象成員
}
成員的定義:與抽象成員的寫法一樣(沒有關鍵字),沒有執行體(方法體);沒有訪問修符;
實現:子類“繼承”自接口,接口可以看做是子類的“干爹”,補全抽象的方法
調用:和類的使用方法一樣,要使用接口中的方法,則將對象賦值給接口變量
多態:和抽象類的實現一樣,接口的多繼承使得多態的實現變得靈活
子類繼承父類,子類實現(繼承)接口:class 子類名:父類名,接口名 或class 類名:接口名
個人理解:接口就是把類中的一種特定的功能性方法封裝成接口(如開車、飛等),聲明類的時候可以繼承這個接口,就擁有了這個接口所特有的功能,就可以在類中實現這個功能,那實現后的這個功能就成為了這個類的一個功能性方法。用接口可以實現多繼承,一個類可以繼承多個接口,也就擁有了多個功能性方法,聲明類的時候可以選擇性的繼承,比如定義老師的時候可以繼承說話、吃飯、教課等接口,定義司機的時候又可以繼承說話、吃飯、開車等接口
//定義一個接口,表示Driving功能
interface IDrivable
{
void Driving();
}
class Teacher:Person,IDrivable//繼承父類和接口
{
public Teacher(string name, int age, string sex):base(name,age,sex)
{//繼承父類的構造方法
}
public void Driving()//實現接口的Driving功能
{
Console.WriteLine("我會開車");
}
public override void ShowMe()//重寫父類方法
{
Console.WriteLine("我是老師");
}
}
static void Main(string[] args)
{
Teacher tea = new Teacher("張三", 18, "男");
tea.ShowMe();//調用重寫后的方法
tea.Driving();//調用實現后的接口方法
Console.ReadKey();
}
9、多態和接口
接口是可以多繼承的,多繼承同時會引起方法的重名
為避免重名,顯示實現接口:
返回值 接口名.方法名(參數)
{
//方法體
}
顯示實現接口的這個方法只能由接口變量進行調用
現階段記住接口使用的語法,然后將接口直接當做抽象類使用(初學階段)
10、值類型和引用類型
變量可以看做是一個數據
值類型,是一個存儲數據的容器,這個數據就是這個類型表示的數據
引用類型,也是一個容器,但是這個容器存儲對象的一個引用(地址),真正的數據在另一塊內存中,就相當於是一個指向數據的快捷方式
值類型存儲在棧里面,引用類型存儲在棧和堆里面(堆里存儲的是真實數據,棧里面存儲的是真實數據在堆里面的內存地址)
值類型和引用類型會涉及到傳參和賦值時的不同,要注意區分
委托(delegate)也是引用類型
值類型賦值賦的是真實的值,引用類型賦值賦的是地址
引用類型是一個變量,兩個快捷方式,修改一個快捷方式會影響另一個快捷方式;值類型是一個變量和一個復制后的變量,修改一個變量不會影響另一個變量
所有的值類型都繼承自System.ValueType類
11、引用傳遞:ref和out
使用ref或out就可以實現將引用傳遞過來
定義方法時,在參數類型前加ref或out
調用方法時,在參數前加ref或out
注:定義變量時不需要加ref或out
當一個變量傳遞給一個方法時,總是復制一份自己的數據,賦值給方法,那么方法中執行的變量就與方法外的那么變量沒有關系了,方法內修改變量值,也不會影響方法外的那個變量。有時會要求多個返回操作(方法中的變量有多個在方法外需要使用),此時可以使用ref標記參數,此時在方法中使用的這個變量與方法外的變量就是同一個變量了,相當於可以使用參數實現返回值
out與ref的作用與使用方法相同
out 必須在方法中賦值
ref必須在方法外賦值
12、類型轉換
隱式類型轉換:兼容類型,小轉大
顯式類型轉換:即強制類型轉換 (轉換后的類型)要轉換的變量
Convert類型轉換:系統封裝的方法,Convert.ToInt32()、Convert.ToDouble()等
其他轉換方法:int.Parse()、int.TryParse()(第一個參數表示待轉換的字符串,第二個out(result)參數表示數字轉換成功后的變量,如果轉換失敗,返回false,為out result賦值為0)
用int.TryParse()代替try-catch,釋放內存,提高性能
不止int類型有TryParse,所有基本類型都有TryParse,用法都一樣
13、異常處理(try-catch、try-catch-finally、try-finally)
Exception是一個專門用來封裝異常的一個類型
兩個屬性:message(異常說明文本)和stackTrace(異常拋出的順序)
拋出異常:throw Exception的一個對象(new一個對象)
從出現異常的try向上依次拋出(方法之間依次調用),直至處理,找到最近的catch捕獲異常(用於try-finally語句,catch塊在方法以外,執行順序是try-finally再向上尋找catch執行異常捕獲)
Finally用於釋放資源,finally{},程序執行完try-catch后會再執行finally語句塊
Finally總會執行,即使try-catch中有return
try
{ }
catch (Exception ex)
{ throw; }
finally
{ }
Try-finally用處不多,用於釋放資源
使用異常會降低系統性能,盡量少用
14、string字符串處理
構造函數:string(char[] chs)、string(char ch,int count)
string str = new string(’a‘,’b’,’c’);//abc
string str = new string(‘a’,3);//aaa
3.144.ToString(“0.0”);用於控制格式,會四舍五入
字符串可以當做數組進行處理(使用下標讀取,擁有數組的各種屬性)
string str = “不可見的你”; 此時str[1]就是“可”了
通過索引得到的數據是char類型,字符串不可改變,使用索引無法修改字符串的數據
要想修改可將字符串變成字符型數組,str.ToCharArray()
string是引用類型
string str = string.Empty;聲明一個空字符串
判斷字符串是否為空:
str.Length == 0(推薦)
str ==“”
str == string.Empty
String.IsNullOrEmpty(要判斷的字符串)(推薦)
15、string常用方法
1)字符串比較:bool isTrue = string.Equals(string a,string b)
String.Equals(str1,str2);
Str1.Equals(str2,StringComparison.OrdinalIgnoreCase); //第二個參數意思是不區分大小寫
string.Compare(str1,str2);
Equals方法:
String重載的方法判斷兩個,一個是==,一個是EqualsHelper
==判斷兩字符串是否相同
EqualsHelper判斷兩個字符串中每一個字符是否相同
Object的重載是來自於object類,是繼承下來的
在object中使用的是==和Equals方法
Equals方法是一個虛方法,由string重寫了,調用的是object.ReferenceEquals方法和EqualsHelper方法
注意:==默認表示判斷兩個對象的地址是否相同,與object.ReferenceEquals方法一樣
String提供的str1.Equals(str2)判斷地址是否相同,字符串是否object提供的virtual Eaulse(object)
Equals和==的區別:
Equals會判斷兩個值的內存地址;==只判斷值是否相等。
Compare方法:
int result = String.Compare(str1,str2);
str1>str2 -> 1 str1 == str2 -> 0 str1<str2 -> -1
string.Compare方法比較兩個字符串,這兩個字符串字母順序表示大小,按照字典排序規則進行比較(對於char類型:A>a,B>a,b>A同一個字母,大寫>小寫,不同字母按照字母順序后面的大於前面的;對於string類型不是按照埃克森碼比較,A>a,b>A,B>a)
2)大小寫轉換:ToLower()和ToUpper()
3)str1 = str1.Trim([params char[] chs]);去除字符串兩邊的空格,或chs中出現的字符(重載)
str1 = str1.TrimEnd(params char[] chs);去除字符串的末尾chs中出現的字符
str1 = str1.TrimStart(params char[] chs);去除字符串的開頭chs中出現的字符
4)合並與分割
合並字符串:string s = string.Join(str1,str2,str3,...)
分割字符串:string[] strArray = str1.Split(‘a’);
string[] strArray= str1.Split(new char[]{‘a’,’b’},StringSplitOptions.RemoveEmptyEntries);去掉a、b和所有空格
5)字符串查找
Contain包含,返回bool類型的值,str1.Contain(“愛”);
IndexOf檢索位置,返回字符串中某個字符的位置(int)找不到則返回-1
str.IndexOf(要找的字符或字符串)
str.IndexOf(要找的字符或字符串,開始尋找的位置)
str.LastIndexOf()是從后往前尋找,用法同str.IndexOf()
注意:IndexOf和LastIndexOf中第二個參數(開始尋找的位置)只是為了說明要查找的是字符串中的哪一個字符(如果字符有重復),即按查找順序在開始位置后的第一個字符,返回的索引依然是該字符在整個字符串中的索引,而不是從查找位置開始的索引。
6)截取子字符串
string s =str1.Substring(開始的位置,子字符串的長度)
String s = str1.Substring(開始的位置)
7)判斷字符串的開頭和結尾
bool a = str1.StratWith(字符串);
bool a = str1.EndWith(字符串);
8)字符串的插入、移除和替換
插入:string str2 = str1.Insert(位置(int),要插入的人字符串(string));
移除:string str2 = str1.Remove(位置(int),長度(int));
string str2 = str1.Remove(位置(int));移除該位置后的所有字符
替換:string str2 = str1.Replace(舊字符串,新字符串);
9)格式化字符串
String.Format(格式化的字符串,填坑的參數)
string str1 = String.Format(“{0},{1}”,str2,str3);
10)判斷字符串是否為空
String.IsNullOrEmpty(str);
10)字符串的方法(總結)
增:添加(+=)、插入(Insert)
刪:Remove
改:Replace、 ToUpper、ToLower、Trim、TrimStart、TrimEnd、Split、Join、new String、SubString、Format
查:Contains、IndexOf、LastIndexOf、StartsWith、EndsWith、ToCharArray、Length、Empty
比:Equals、Compare
16、StringBuilder
大量字符串拼接的時候性能非常差,很難完成大型數據的拼接
(Stopwatch對象可以記錄程序運行的時間,Start開始計時,Stop結束)
使用StringBuilder:StringBuilder sb = new StringBuilder();
sb.Append(要添加的字符串);//向sb中添加數據
sb.AppendLine(要添加的字符串);//向sb中添加數據后換行,等價於sb.Append(要添加的字符串+”\r\n”);
sb.AppendFormat(要添加的字符串);格式化處理,等價於 sb.Append(string.Format(要添加的字符串));
string str = sb.ToString;輸出或賦值時要轉換成string
在處理大型數據的時候,StringBuilder要比普通的string處理快很多,因為處理大型數據要用StringBuilder
17、讀取文件
string[] lines = File.ReadAllLines(@"H:\傳智播客實訓資料代碼\第9天\a.csv", Encoding.Default);//按行讀取
string[] lines = File.ReadAllText(@"H:\傳智播客實訓資料代碼\第9天\a.csv", Encoding.Default);//讀取所有文本
string[] lines = File.ReadLines(@"H:\傳智播客實訓資料代碼\第9天\a.csv", Encoding.Default);//讀取一行文本
string[] lines = File.ReadAllBytes(@"H:\傳智播客實訓資料代碼\第9天\a.csv", Encoding.Default);//讀取字節
18、文本高亮
->讓文本框獲得焦點--txtContent.Focus();
->找到要高亮的位置--pos
->選中(高亮)的字符串長度--length
->調用TextBox的選中方法txtContent.Select(pos,length)
19、泛型集合 List<T>和Dictionary<TKey,TValue>
List集合:動態的自定義數組
List<類型> 集合名 = new List<類型>();
List用法與ArrayList一樣,不同點在於定義時和使用時的類型固定,因此可以完全替代ArrayList
類型[] = list.ToArray()//將List轉換成該類型的數組
Dictionary集合:動態的自定義鍵值對
Dictionary<類型,類型> 集合名 = new Dictionary<類型,類型>();
Dictionary用法與Hashtable一樣,不同點在於定義時和使用時的類型固定,因此可以完全替代Hashtable
20、裝箱與拆箱
直接將值類型賦值給引用類型就是裝箱
將存儲值類型的引用類型強制轉化為對應的值類型就稱為拆箱
Object o = 1;//裝箱 int n = (int)o;//拆箱
裝箱對值類型保持無關性,裝箱對引用類型保持相關性
裝箱和拆箱會對性能有一定的影響
21、讀寫數據(I/O操作、System.IO類)
System.IO.Path類,專門處理字符串路徑,是一個靜態類
string path = @“D:\window\system\file.txt”;
獲取文件名:string fileName = Path.GetFileName(path);
獲取后綴名:string fileName = Path.GetExtension(path);
修改后綴名:path = Path.ChangeExtension(待修改的路徑,“mp3”);
獲取文件夾名:string fileName = Path.GetDirectoryName(path);
合並路徑:path= Path.Combine(路徑1,路徑2,路徑3...);
臨時文件夾:path = Path.GetTempPath();
絕對路徑:從盤符開始的最精確的路徑
相對路徑:相對於當前文件同一個目錄作為參照的路徑
相對路徑和絕對路徑:區分就看有沒有根目錄盤符的標記
22、文件流(FileStream)
1)引入命名空間
2)創建FileStream對象FIleStream fs = new FileStream(文件名,FileModel,FileAccess)
FileStream fs = new FileStream(@"D:\a.txt", FileMode.Create, FileAccess.Write);
FileModel:對文件的處理方式(打開、創建、追加)
FileAccess:對文件的操作:讀、寫
3)使用方法操作
寫一個字節:fs.WriteByte()
寫一個字節片段(將字節數組中的數據寫入到fs指定的文件):
fs.Write(存放字節的數組,開始寫字節的數組下標,要寫的字節數)
讀一個字節:fs.ReadByte()
讀一個字節片段(將fs指定的文件中的數據讀取到字節數組):
fs.Read(存放字節的數組,開始存放字節的數組下標,要讀的字節數)
4)釋放資源
fs.Close()和fs.Dispose()方法(兩個都要調)
23、StreamReader和StreamWriter
StreamReader:專門用來讀取文本文件的流
1)引入命名空間:IO
2)new一個FileStream指向文件
3)new一個StreamReader,構造函數參數是FileStream的對象
4)調用方法
StreamReader有一個屬性EndOfStream:判斷當前的流位置是否在文件流的末端,一般用來判斷文件是否讀完
StreamWriter:專門用來寫入文本文件的流
1)引入命名空間:IO
2)new一個FileStream指向文件
3)new一個StreamWriter,構造函數參數是FileStream的對象
4)調用方法
24、File和Directory對文件和目錄操作做了一個封裝
增(Create):創建文件或文件夾
刪(Delete):刪除文件或文件夾
改:改名字 File.Move(“1.txt”,“2.txt”)
查:...
判斷是否存在(Exists)
移動文件:File.Move(“1.txt”,“2\1.txt”)
復制文件:Copy
Directory用於處理文件夾(目錄)
刪除目錄的時候如果目錄不是空的則會報錯,此時需要在Delete內添加一個true
Directory.Delete(“1”,true);
25、序列化:
序列化:把一個對象的內容存儲到磁盤上(文件流)、網絡上(網絡流)、內存里(內存流),下次需要用到對象數據的時候可以直接拿來用
反序列化:把已經序列化存儲到磁盤、網絡、內存里的對象數據,提取到項目中的對象中
序列化步驟:
1)創建一個文件流
2)為需要序列化的對象添加標記([Serializable]表示此對象可以被序列化)
3)創建BinaryFormatter(導入命名空間)
4)調用Seralize方法(bf.Seralize(文件流對象,需要被序列化的對象))
反序列化步驟:
1)創建一個文件流
2)為需要序列化的對象添加標記([Serializable]表示此對象可以被序列化)
3)創建BinaryFormatter(導入命名空間)
4)調用DeSeralize方法(object ob = bf.DeSeralize(文件流對象))
反序列化定義接收類型的時候必須根據原序列化的程序集確定類型
建議:在使用序列化的時候盡量避免使用自動屬性,因為自動屬性在每次編譯的時候自動生成的字段名可能不一樣,所以在反序列化的時候可能會造成問題
注意:如果序列化和反序列化不在同一項目下,那反序列化的時候需要引用原序列化的程序集
26、關於文件夾下的文件與子文件夾
Directory類的靜態方法
獲取子文件名:string[] s = Directory.GetFiles(@“D:\dir“);
獲取特定后綴名的子文件名:string[] s = Directory.GetFiles(@“D:\dir“,”*.txt“);
獲取所有文件夾名:string[] s = Directory.GetDirectories(@“D:\dir“);
27、正則表達式
元字符:
1).(點)表示除\n以外任意的單個字符
2)[ ]表示括號內的任意一個字符(如a[xyz]b表示axb或ayb或azb)
3)^在[ ]括號內表示取反(如^[a-z]表示除小寫字母以外的所有字符)
4)^匹配一串字符的開始(^abc表示以abc為開頭的任意字符串)
5)$匹配一串字符的結尾(abc$表示以abc為結尾的任意字符串)
5)|表示“或”,優先級最低(如a|bc表示a或bc,(a|b)c表示ac或bc)
6)()可以改變優先級、提取組
7)*表示限定前面的字符出現任意次數(abc*表示ab、abc、abcccc......(abc)*表示abc、abcabc)
8)+ 至少出現一次,也可出現多次(xa+y表示xay、xaay...)
9)?表示出現0次或1次
10){n}限定前面的表達式出現n次
11){n,m}至少出現n次,最多出現m次
12){n,}至少出現n次
13)\d表示0-9,\D表示\d的反面
14)\s表示空白符,\S表示\s的反面
15)\w表示數字、字母、下划線、漢字,\W表示\w的反面
注:正則表達式的轉義符也是\,如果要表示*(星),可以寫成\*,要表示一個\,可以寫成\\
28、Regex類
Regex.IsMatch(待判斷的字符串,正則表達式)判斷一個字符串是否匹配某個正則表達式
!如果正則表達式沒有^和$,那默認不設定開頭和結束,此時只要待判斷的字符串中含有該正則表達式就返回true
^z|food$:表示以z開頭的任意字符串或者以food結尾的任意字符串,都返回true
因為|的優先級最低,所以上邊的表達式其實是(^z)|(food$)
Regex.Match()從某個字符串中提取匹配某個正則表達式的某個子字符串,但只能提取一個
Regex.Matches()同上,可以提取所有的匹配字符串
Regex.Replace()字符串替換,把所有匹配正則表達式的字符串替換為對應的字符
29、字符串提取
字符串提取的時候一般不加^和$
Match m = Regex.Match(原字符串,要提取的正則表達式)
m.Value屬性表示提取到的字符串
m.Group[n]:提取組,按正則表達式中()括號對的個數,m.Group[0]表示整個字符串,m.Group[1]表示第一個括號內的內容,所以提取組的時候要從下標1的地方開始
MatchCollection mc = Regex.Matches(原字符串,要提取的正則表達式)
MatchCollection是一個集合,需要foreach遍歷輸出(遍歷時的類型是Match)
30、貪婪模式
定義:當正則表達式提取的時候,如果一個字符或多個字符都能匹配,這時會按照使用最多字符的方式來匹配。
正則表達式默認采用貪婪模式,盡多的匹配
在限定符后面使用問號用來終止貪婪模式,即只會提取匹配的第一個
string s = “aaaa bbbbbbbb”;string s1 = “[a-zA-Z]+”; 匹配結果:aaaa
string s = “aaaa bbbbbbbb”;string s1 = “[a-zA-Z]+?”; 匹配結果:a
31、字符串替換
String s =Regex.Replace(原字符串,待替換的正則表達式,替換后的字符串);
如:string s = Regex.Replace(s, "-+", "-");//把字符串s中的所有-替換成一個-
提取組替換:
string s = "aaa13812345678bbb";
s = Regex.Replace(s, @"(\d{3})(\d{4})(\d{4})", "$1****$3");//把手機號中間4位替換成4個*
32、byte數組和字符串的轉換
byte[] bt= System.Text.Encoding.UTF8.GetBytes(str);
string str = System.Text.Encoding.UTF8.GetString(bt);
33、委托(delegate)
委托是一種數據類型(和類一樣),可以將方法賦值給委托對象,可以理解為方法類型
定義(在命名空間下,或添加一個cs文件,和類的定義一樣):
1)使用delegate關鍵字
2)這個委托將來要存儲的方法如果沒有返回值,那么委托也要定義成void,參數也要與方法保持一致
3)委托是一個數據類型,用的時候需要傳遞一個變量
4)創建委托對象的時候可以不用new,系統編譯時會自動給我們new
定義委托類型:public delegate void ChangeTxtDelegate(string str);
創建委托對象:public ChangeTxtDelegate changeTxt; //Form1中
給委托賦值:f1.changeTxt = ChangeText; //Form2中
調用委托:changeTxt(txt); //Form1中,此種調用方法為簡寫,實際上是changeTxt.invoke(txt);
創建委托對象時也可直接對其賦值:ChangeTxtDelegate changeTxt = new ChangeTxtDelegate(ChangeText) ;
用法(窗口間傳值):委托要聲明在需要被傳值的對象內,比如Form1要提取Form2中的數據(即Form2往Form1傳值),就應該把委托聲明在Form1中,在Form2中寫一個方法獲取要傳的數據,並且賦值給Form1的委托,最后在Form1中調用委托就ok了。(委托聲明位置不絕對,還需要考慮當前活動窗口的問題,要聲明再活動窗口中,有時也可聲明在被提取值的窗口內)
作用:可以在某個代碼內部,嵌入一段外部代碼(外部寫一個方法賦值給委托或在定義方法時把委托類型作為參數,調用時把方法名作為參數傳遞給委托對象,那么委托就執行該方法,不同的外部項目可以寫不同的方法賦值給委托,可以實現一個委托選擇性執行多種方法的靈活性),相當於是注入
什么情況下使用委托?
當一個類型中需要嵌入一段代碼,但是這段代碼具有不確定性,是根據使用這個類型的用戶來確定代碼的,這時就可以在該類型中使用一個委托,保證在某種情況下會調用該委托,用戶將對應的方法傳遞給該委托,就會調用這個方法
可以把靜態方法或者私有方法賦值給委托了變量,賦值后只要能使用到該委托變量的地方就能使用該方法,打破了訪問修飾符的限制
一般在調用委托前或者觸發事件前,都要判斷委托變量或事件是否為null,如果為null則不調用
34、多播委托
MyDelegate md = new MyDelegate(M1); md+=M2;(增加某個方法)md-=M4;(去掉某個方法)
使用委托時,如果不是+=而是直接用=賦值,則會將前面增加的所有方法都覆蓋掉
多播委托可以存儲多個方法,委托中方法調用的順序與增加方法時的順序是一致的,但是,不要依賴於這個順序(微軟沒有承諾這樣的順序,萬一以后改了就GG了)
多播委托中,如果要是有返回值,只會得到最后一個方法返回的結果。可以通過調用GetInvocationList()方法返回當前委托中的所有方法,返回值類型時一個Delegate數組(委托數組),再用foreach遍歷調用所有方法
所有定義的委托都繼承自抽象類MulticastDelegate,而MulticastDelegate又繼承自Delegate抽象類
多播委托內部是將綁定在當前委托對象上的每個方法,都轉換成一個委托對象,並且存儲在了一個叫_invocationList的object數組中。當調用委托的時候,其實就是循環遍歷_invocationList數組,並且調用其中的每個委托
多播委托中,如果其中的某個方法執行時發生了異常,則后面的方法不再執行。
多播委托中只能存儲同一類型的委托,即返回值、參數都相同
35、事件
事件是在特定條件下執行的動作,這個動作是由用戶確定的。如Button的Click事件,其動作是由程序員寫的,因此可以用委托來實現這個功能,寫不同的方法賦值給委托變量。
但用委托模擬方法有兩個缺點:
1)定義的委托可以在類外部的任何地方被觸發,即非特定條件下也可以調用。因為委托變量是public修飾,改為private就不能在外部賦值了。
2)由於委托可以用=(等號)賦值,所以就有可能把前面已經注冊的事件處理程序都覆蓋掉。
而用事件就可以解決以上問題:
1)用事件必須先定義一個委托,否則無法使用事件
2)實例化委托變量的時候,在委托類型名前面、訪問修飾付的后面加event關鍵字,就定義了一個事件
3)使用方法與委托一樣,只是避免了以上兩個問題
4)由於事件只能有+=或-=賦值,所以就避免了用=(等號)賦值時覆蓋前面事件處理程序的問題
5)由於事件不能在定義事件的類以外被觸發,所以也就避免了冒充事件觸發的問題。
36、委托和事件的區別(面試常考)
委托和事件沒有可比性,委托是數據類型,事件是委托類型的變量
委托可以用+=、-=、=賦值,可以在類的外部被調用
事件只能用+=、-=賦值,不能在類的外部被調用
事件的內部是用委托實現的
事件的作用和委托變量(不是委托,是委托變量)一樣,只是在功能上受到一定的限制
37、var類型
var只能用作局部變量,可以賦任何類型的值。
var a = 1;和int a = 1;兩者完全一樣
var不能作為類的成員的類型,不能用作參數,不能用作返回值,只能用作局部變量
38、擴展方法(不修改微軟系統方法源代碼,實現系統方法的增加)
1)添加一個靜態類
2)在靜態類中增加一個靜態方法,方法參數是:(this+要擴展的類名+變量名,該方法的參數)
如要在string類中添加一個驗證是否是Email格式的擴展方法:
public static bool IsEmail(this string str)
{
return Regex.IsMatch(str, @"^\w+@\w+\.\w+$");
}
string a = "1321321@qq.com";
Console.WriteLine(a.IsEmail());
擴展方法只是看起來像類中的方法,其實不是,所以在擴展方法中也訪問不到類中原來的私有成員
因為會擾亂系統方法,所以不建議使用,盡量不用
39、XML
XML是用一種格式化的方式來存儲數據,可以用記事本打開
1)XML有且只有一個根元素
2)有開始標簽就必須得有結束標簽,且區分大小寫
3)元素的屬性值要用引號
讀取XML文件,通過XDocument(需要添加命名空間)
讀取整個XML文件: XDocument xd = XDocument.Load(XML文件路徑)
循環遍歷每個節點:
1)獲取根節點 XElement xeRoot = xd.Root;(xeRoot.ToString()表示XML文檔的內容)
2)遞歸遍歷XML中的每個節點
遍歷xeRoot下面的所有子節點:
public static void GetEle(XElement xe)
{
foreach (XElement ele in xe.Elements())
{
Console.WriteLine(ele.Name);
GetEle(ele);
}
}
GetEle(xeRoot);
例:讀取XML數據
<CFX>
<MSG>
<交易碼val="1000" />
<流水號val="100000000000000001" />
<金額val="1234567890.12" />
<付款機構val="1234" />
<付款單位賬號val="12345678901234567890" />
<收款機構val="1234" />
<收款單位賬號val="12345678901234567890" />
</MSG>
<MSG>
<交易碼val="1000" />
<流水號val="100000000000000002" />
<金額val="1234567890.12" />
<付款機構val="1234" />
<付款單位賬號val="12345678901234567890" />
<收款機構val="1234" />
<收款單位賬號val="12345678901234567890" />
</MSG>
</CFX>
VS中讀取XML的數據:
XDocument xd = XDocument.Load("ytbank.xml"); //加載XML文件
XElement xeRoot = xd.Root; //獲取根節點
foreach (XElement item in xeRoot.Elements()) //遍歷根節點,獲取第一級子節點(MSG)
{
foreach (XElement itema in item.Elements()) //遍歷第一級子節點,獲取第二級子節點
{
Console.WriteLine("{0}:{1}", item.Element(itema.Name).Name, item.Element(itema.Name).Attribute("val").Value); //獲取節點名和屬性值
}
Console.WriteLine("==================================");
}
XML寫入:
XDocument xdoc = new XDocument(); //創建一個XML對象
XElement xeleRoot = new XElement("Websites"); //創建一個根節點
xdoc.Add(xeleRoot); //將根節點添加到XML文件中
xeleRoot.SetElementValue("baidu", "http://www.baidu.com"); //添加一個節點以及內容
XElement xeleGoogle = new XElement("website"); //創建一個節點
xeleGoogle.SetAttributeValue("url", "http://www.google.com"); //添加屬性和屬性值
xeleGoogle.SetElementValue("name", "谷歌"); //為節點添加子節點
xeleGoogle.SetElementValue("age", "14");
xeleGoogle.SetElementValue("boss", "謝蓋爾");
xeleRoot.Add(xeleGoogle); //將節點添加進根節點
xdoc.Save("website.xml"); //保存一個XML文檔路徑