c#編譯器把源碼編譯成IL(中間)代碼,再由CLR經過即時編譯器把IL代碼編譯成本機機器語言(平臺語言)
www.webkaka.com//測試服務器的網速
html
ctrl+k+d 代碼對其 CTRL+K+S #REGION代碼摺疊 ctrl+r+e 連敲2個回車:封裝字段 shift+alt+f10:實現接口
XML序列化:把對象寫到XML文檔中
標識某個對象的某個字段不能被序列化,只要把[XmlIgnoreAttribute]放在某個字段前面
二進制流序列化:把對象序列化成二進制流進行網絡傳輸
標識某個對象的某個字段不能被序列化,只要把[NonSerialized]放在某個字段前面
「序列化」可被定義爲將對象的狀態存儲到存儲媒介中的過程。在此過程當中,對象的公共字段和私有字段以及類的名稱(包括包含該類的程序集)都被轉換爲字節流,而後寫入數據流。在之後「反序列化」該對象時,建立原始對象的精確複本。
1、爲何要選擇序列化
一個緣由是將對象的狀態保持在存儲媒體中,以即可以在之後從新建立精確的副本;
另外一個緣由是經過值將對象從一個應用程序域發送到另外一個應用程序域中。
例如,序列化可用於在 ASP.NET 中保存會話狀態並將對象複製到 Windows 窗體的剪貼板中。
遠程處理還可使用序列化經過值將對象從一個應用程序域傳遞到另外一個應用程序域中。
序列化的目的: 一、以某種存儲形式使自定義對象持久化;
二、將對象從一個地方傳遞到另外一個地方。
實質上序列化機制是將類的值轉化爲一個通常的(即連續的)字節流,而後就能夠將該流寫到磁盤文件或任何其餘流化目標上程序員
XML的通常用處:配置文件,保存靜態數據,通常用於webservice和webconfig文件中web
反射:程序運行時,動態獲取程序集的相關信息,類,方法等。動態建立的類的實例,調用類中的方法,訪問類中的屬性。
通常用於:開發插件,動態加載程序組件,動態更換底層數據庫類型正則表達式
hashset用來存放不重複的數據
hashset<int> set=new hashset<int>()
set.add(5);
set.add(3);
set.add(5);
//最後輸出沒有重複的數據算法
靜態變量的存儲字段到程序結束的時候纔回收,在整個項目中至關於全局變量,若是用多了,會很佔內存
Random r = new Random();
r.Next(1,16);//返回一個1到15非負的隨機數
r.Next()///返回一個非負的隨機數
r.Next(151)//返回一個0到150的非負隨機數數據庫
各類變量的默認值:bool:false,數字int:0,字符char:\0,引用類型(string):null編程
在監視窗口中
值類型前面加個&,查看內存地址
引用類型前面加個*,查看內存地址c#
32位計算機,用32個二進制位來表示一個整數,同時使用32個二進制位來表示一個內存地址
一個內存空間放運行方法中的代碼--線程棧
一個內存空間放對象名--託管堆
存放大對象,靜態對象
每一個INT佔4個字節,64位8個字節數組
垃圾收集器:手動調用,system.gc.collect(0);會根據你係統當前的內存的使用狀況進行優化
每次進行緩存
屬性:
//通常的屬性,本質就是一個方法,後臺提供的get,set的方法
private static string ploginname; public static string Ploginname { get; set; }
//自動屬性,方便的時候用,只是存取單個值,沒有複雜的邏輯的功能,沒有特殊的控制要求的時候
//get和set必須成對存在
//系統後臺生成Type字段,用於保字段的值
public static string Type { get; set; }
//抽象屬性 在抽象類中存在的
//抽象屬性與自動屬性很相識,只是在前面加了個abstract來修飾而已
//後臺沒有方法體,所以能夠是隻讀,而自動屬性不能夠,必須get和set成對出現
abstract class MyBase { public abstract string Test//要求子類重寫 { get; set; } //抽象的只讀屬性 public abstract string Test2 { get; } public abstract string Test1 { get; set; } } class MySub : MyBase { string test;//子類必須提供字段 public override string Test//重寫屬性 { get { return test; } set { test = value; } } //此時重寫基類的屬性,變成了自動屬性,後臺生成字段來保存變量的值 public override string Test1 { get; set; } //重寫的時候須要注意,子類只重寫父類提供的,若是父類只提供了只讀屬性,則子類 //也只能重寫只讀的,而不能加set進去 public abstract string Test2 { get{return test;} } }
棧:由編譯器自動分配、釋放。在函數體中定義的變量一般在棧上,通常存放值類型的變量。
堆:通常由程序員分配釋放。用new、malloc等分配內存函數分配獲得的就是在堆上,通常存放引用類型。
構造函數(方法):
聲明空構造函數可阻止自動生成默認構造函數。注意,若是您不對構造函數使用訪問修飾符,則在默認狀況下它仍爲私有構造函數。
可是,一般顯式地使用 private 修飾符來清楚地代表該類不能被實例化。
1、沒有繼承時:
一、默認構造方法(無參數構造方法)
一、沒有提供構造方法的時候,系統自動添加一個無參數的構造方法,若是已經寫了構造方法,那麼系統就不會自動添加默認構造方法,
須要本身手動添加
構造方法的重載
一、當調用的指定構造函數後有this的時候,會先調用this表示的構造方法,再調用本身的
2、有繼承關係的時候:
一、默認調用父類的無參構造方法(後面只是隱藏了base:())
二、使用base能夠指定調用父類的構造方法
一、繼承時,子類能夠選擇性的調用基類的構造函數(經過函數的重載),子類:base(i)
二、繼承時,子類構造函數默認繼承基類的無參構造函數,子類:base(),此時必須保證基類有無參構造函數
三、類中,原始構造函數初始化時,使用 構造函數:this(),來繼承最原始的默認構造函數
四、在建立對象的時候,先調用父類構造方法,再調用子類構造方法
五、在調用構造方法的時候,其實默認有一個base(),調用了父類無參的構造方法
public MyClass() { InitialCompoment();//原始構造方法,有這個類中全部字段的初始化操做 } public MyClass(string n):this() { // 再調用帶有參數string n這個構造方法以前,先去調用標準的構造方法 Console.WriteLine(n); }
base關鍵字:一、表示父類 二、父類的構造方法(指定調用哪個父類構造函數)
this關鍵字:一、表示當前類 二、表示當前構造方法(指定先調用哪個當前類的構造函數)
繼承關係中的構造函數:
// 子類與父類構造方法的執行順序:先調用父類的,再調用子類的
// 如何根據父類構造方法的重載,進行指定調用:使用base:()//public SubClass():base("1")
// 使用構造方法初始化各個字段(在繼承中經過參數傳值)
public Student(string name, int age, char gender, int score) // 子類構造方法的定義 : base(age, gender, name) // 父類構造方法的調用 { // 調用子類構造方法時,先調用父類構造方法,這裏的base是在調用父類構造方法 _score = score; }
不存在繼承關係的構造函數:
//當構造函數中後面還有:this的時候,在調用自己以前回去調用:後面的構造函數,當new MyClass()的時候會去調用 MyClass(int i),在調用MyClass(int i)時候會跑去調用MyClass(int i, int j)
//而後再一個個返回執行本身
//以下所示:
class MyClass { public MyClass():this(3) { Console.Write("1"); } public MyClass(int i):this(2,3) { Console.Write(i); } public MyClass(int i, int j) { Console.Write("{0}{1}", i, j); } } class Program { static void Main(string[] args) { new MyClass();//輸出結果爲23 3 1 Console.ReadKey(); } } //InitialCompoment()原始構造方法 public MyClass() { InitialCompoment(); } public MyClass(string n):this() { // 再調用帶有參數string n這個構造方法以前,先去調用標準的構造方法 Console.WriteLine(n); }
類是對某個具體對象的抽象、對象是類的實例化
接口的對功能的抽象
接口:
一樣的方法對不一樣的對象表現爲不一樣的行爲,同一操做做用於不一樣的對象,產生不一樣的執行結果。
一、類的定義的通常格式:[public][static] class 類名:[基類],[接口1],[接口2],..
二、接口的語法:
一、接口聲明以I開頭
二、接口中的方法沒有修飾符
三、無實現體
四、接口能夠繼承多個接口 接口:接口1,接口2,接口3
顯示實現接口:2個接口包含相同的方法時,要實現方法時,直接接口1.方法名,去掉前面的修飾符
包含的成員:方法、屬性、事件、索引(沒有字段)
接口只能定義方法(只能定義行爲,不能定義實現也就是字段),由於事件、索引器、屬性本質上都是方法,因此接口中也能夠定義事件、索引器、屬性。
ASCCI:1字節
UNICODE:任何都佔2個字節
接口與類是平行關係,接口不繼承Obect,通常類也不實現與接口
里氏轉化:(父類的引用指向子類的對象) // 一、子類能夠直接賦值給父類(子類對象能夠直接轉化爲父類對象) // 二、若是知足第一個條件的父類對象,能夠強制轉化爲原來的子類對象 父類 父類對象 = new 子類(); 子類 子類對象 = (子類)父類對象; Protocal protocal = new iPhone6(); iPhone6 i = (iPhone6)protocal; //子類必須能替換父類,反過來不必定,父類能替換子類的前提是:該父類對象是指向該子類對象 子類 子類變量=父類 as 子類 子類 子類變量; if(父類變量 is 子類) { 子類 子類變量=(子類)父類變量; } else { 子類 子類變量=null; }
靜態類的主要特性:
•僅包含靜態成員。
•沒法實例化。
•是密封的。
•不能包含實例構造函數。
所以,建立靜態類與建立僅包含靜態成員和私有構造函數的類基本相同。私有構造函數阻止類被實例化。使用靜態類的優勢在於,編譯器可以執行檢查以確保不致偶然地添加實例成員。編譯器將保證不會建立此類的實利。
靜態類是密封的,所以不可被繼承。它們不能從除 Object 外的任何類中繼承。靜態類不能包含實例構造函數,但能夠包含靜態構造函數。若是非靜態類包含須要進行重要的初始化的靜態成員,也應定義靜態構造函數。
靜態成員:
非靜態類能夠包含靜態的方法、字段、屬性或事件。即便沒有建立類的實例,也能夠調用該類中的靜態成員。始終經過類名而不是實例名稱訪問靜態成員。不管對一個類建立多少個實例,它的靜態成員都只有一個副本。靜態方法和屬性不能訪問其包含類型中的非靜態字段和事件,而且不能訪問任何對象的實例變量(除非在方法參數中顯式傳遞)。
更常見的作法是聲明具備一些靜態成員的非靜態類,而不是將整個類聲明爲靜態類。靜態字段有兩個常見的用法:一是記錄已實例化對象的個數,二是存儲必須在全部實例之間共享的值。
靜態方法能夠被重載但不能被重寫,由於它們屬於類,不屬於類的任何實例。
字符串:
string str= string .Empty();//堆內存在空字符; str=null;//沒有開闢堆內存,不指向任何地址。 str.isNULLorEmpty();判斷是否爲空。 字符串的不可變性: 每次建立一個字符串的時候都開闢一個不一樣的內存地址 char[] ch={'1','2'}; string str3=new string(ch); 字符串拘留池(暫存池) string str1="123"; string str2="123";//若是存在大量字符串值相同,則內存地址相同,只建立一個地址。 此時的str1,str2,內存地址相同 因此通常是隻要暫存池
1、比較:相等、比較相等:「==」、Equals方法 2、修整:去掉先後空格,去掉前空格、去掉後空格、去掉數組中的字符:trim() 3、檢索:插入、移除:insert/remove 4、分割與合併:spit()/jion() 5、格式化:string str = string.Format("{0}",1);
//把list對象中內容進行換行輸出的辦法
1、List<string> list=new List<string>(); list.add(); textBox3.Text = string.Join("\r\n", list.ToArray());//把list先轉化成數值,而後用換行符進行鏈接便可 2、StringBuilder sb = new StringBuilder(); for (int i = 0; i < list.Count; i++) { sb.AppendLine(list[i]); } textBox3.Text = sb.ToString(); sb.AppendFormat("{0}\t{1}\t{2}\r\n",1,2,3,4);//使用sb的格式化來追加字符
@符號:預格式字符串:代碼中字符串容許換行,取消轉義字符,保存到文本中的內容也是換行顯示的
c#中的\表示轉義字符,若要表示文本中的\,就必須前面加一個\,如\\,或者是在前面加一個@符號,來取消轉義功能,就表示文本中的\。
\r\n:換行,\":雙引號,\\:一個\,\\\\:表示\\,\b:向前移一個位置,擋掉前面的,\t: 表示一個空格tab,\s:空格
Mian()方法:文件名 參數1 參數2 「1 2」//第一個參數文件名,後面參數以空格來間隔,要包含空格的能夠用「」來包含,在cmd命令中運行:文件名 參數1 參數2 「1 2」
string str = "我¨°又??變à?帥?ì了¢?";
//string轉字節
// byte[] bytes = Encoding.UTF8.GetBytes(str);//把字符串str以編碼UTF8的格式轉化成字節數組
byte[] bytes = Encoding.Unicode.GetBytes(str);//
字節轉string
string temp = Encoding.Unicode.GetString(bytes);//用什麼方法轉的就用什麼方法解
Path類:針對字符串進行處理,無論路徑是否存在
string path=@"d:\music\天黑黑.mp3";
1、改後綴名:path=Path.ChangeExtension(path,"lrc"); 2、得到文件所在的文件夾:Path.GetDirectoryName(path)//d:\music 3、獲取文件名稱:Path.GetFileName(path);//天黑黑.mp3//也能夠獲取文件夾的名字 4、獲取後綴名:Path.GetExtension(path);//.mp3 5、拼接2個路徑:Path.Combine("D:\\1\\music","真的愛你.mp3");//D:\1\music\真的愛你.mp3 Path.Combine("D:\\1\\music\\","真的愛你.mp3")//前面最後結尾處加不加\\,效果同上D:\1\music\真的愛你.mp3 Path.Combine("D:\\1\\music\\","C:\真的愛你.mp3")//返回後面一個C:\真的愛你.mp3 6、獲取全路徑:Path.GetFullpath(文件名);//通常用的較多 string path = Path.GetFullPath("txt");//獲取「txt」文件夾的全路徑:C:\Users\Administrator\Documents\Visual Studio 2008\Projects\福爾摩斯小說閱讀器\福爾摩斯小說閱讀器\bin\Debug\txt 7、獲取臨時文件路徑:Path.GetTempPath();//通常是C盤下面的temp文件 8、獲取臨時文件路徑名稱:Path.GetTempFileName()//獲取C盤下面的temp文件臨時文件名稱
啓動一個進程:
System.Diagnostics.Process p=new System.Diagnostics.Process(); p.StartInfo.Arguments="path";//全路徑 p.StartInfo.FileName="notepad";//應用程序名 p.start(); FileStream類: FileStream("path",FileMode); FileStream("path",FileMode,FileAccess);
讀:
FileStream fs=FileStream("path",FileMode,FileAccess); int res=fs.ReadByte();//按文本中的內容,字節一個一個的讀取,返回的是ASIC編碼,如文本中是1,返回49,此時要顯示出來1要進行轉換(char)res; //採用循環來讀取文本中的內容:while((res=fs.ReadByte())>=0) { cw((char)res); } Read(byte緩存數組,數組中的位置,讀取多少數據);
寫:
WriteByte(byte)//將一個字節寫入到流裏去 fs.WriteByte(97)//等於就是寫了一個a到文本中去 for(int i=97;i<97+26;i++) { fs.WriteByte((byte)i);//寫26個字母到文本中,int 4個字節,byte 2個字節,全部要(類型兼容)強轉 } Write(byte緩存數組,數組中的位置,讀取多少數據);
內存流與網絡流
MemoryStream/NetworkStream
讀寫流
StreamRead/StreamWrite針對文本文件
先實例化一個文件流:FileStream fs=new FileStream(path,mode,access);
在實例換一個StreamRead sr=new StreamRead(fs,Encoding.Default);//出現亂碼時,重載函數加編碼進去或者(fs,Encoding.GetEncoding("gb2312"))用國標碼來獲取中文
sr.ReadLine//一行一行讀;
sr.ReadToEnd//從開始讀到結束
//寫:write.Write(string);
write.WriteLine(?);寫一行
string[] files = Directory.GetFiles("dll", "*dll");
Assembly asm=Assembly.LoadFile(Path.GetFullPath(files[i]));
if (File.Exists("CL.dll"))
{
string path = Assembly.GetExecutingAssembly().Location;//獲取應用程序目錄
path = Path.GetDirectoryName(path);//獲取不包括程序名.exe的目錄
string newpath = Path.Combine(path, "CL.dll");拼接
Assembly ms = Assembly.LoadFile(newpath);
}
文件操做
得到當前目錄:Directory.getCurrentDirectory();路徑容易被改動由於可使用set方法進行修改,通常不用 path=Assembly.getExectingAssembly().lacation;得到當前的應用程序目錄(包括應用程序的名稱:123.exe) path=Path.GetDirectoryName(path)//根據上面得到的路徑來獲取應用程序所在的目錄 path.getcurrentDirectory(); Application.StartupPath:程序啓動的路徑 對文件的操做: File.Creat(path); File.Delete(path); File.AppendLines(path,string); File.AppendAllText(path,string,Encode);//編碼不寫默認是UTF8 File.ReadAllLines(path,Encoding.Default);//返回一個字符串類型的數值 File.ReadAllText(path,Encoding.Default);//讀中文的時候有亂碼得加Encoding.Default 對文件夾的操做 Directory Directory.Creat(path); Directory.Delete(path);//此時若是文件夾中存在文件則會報錯 Directory.Delete(path,True)//此時若是文件夾中存在文件,照樣刪除 Directory.GetFiles(文件夾path);//返回當前文件夾中的全部文件 string[] fs = Directory.GetFiles("txt","*.txt");//txt\柯南·道爾簡介.txt:傳哪一個目錄進去,就能夠獲取該目錄下文件(不包括子文件夾) Directory.GetFiles(文件夾path,"*.txt")//刷選當前目錄下的全部.txt的文件 Directory.GetDirectories(文件夾path);//返回當前目錄下全部(包含子文件夾)的文件 通常狀況下少用靜態類,對大量的文件及文件夾進行操做的時候用File/Directory FileInfo與DirectoryInfo功能與上面2個相同,只是得先建立對象的實例,才能調用 通常對同一個文件進行操做的時候用 string path6 = Assembly.GetExecutingAssembly().Location;//C:\Users\Administrator\Documents\Visual Studio 2008\Projects\獲取各類文件路徑的操做\獲取各類文件路徑的操做\bin\Debug\獲取各類文件路徑的操做.exe string path = Path.GetDirectoryName(path6);//根據上面獲取的完整的應用程序來獲取當前應用程序或文件的目錄,C:\Users\Administrator\Documents\Visual Studio 2008\Projects\獲取各類文件路徑的操做\獲取各類文件路徑的操做\bin\Debug string path1 = Path.GetExtension("txt.txt");//獲取擴展名.txt //Path.Combine(path7, path8);//鏈接路徑 //Path.ChangeExtension(path9, ".dll");//修改擴展名 string path4 = Directory.GetCurrentDirectory();//獲取應用程序的目錄bug目錄,這種模式不安全,通常不用,很容易被修改, C:\Users\Administrator\Documents\Visual Studio 2008\Projects\獲取各類文件路徑的操做\獲取各類文件路徑的操做\bin\Debug // Directory.SetCurrentDirectory("G:\\");//容易被修改 string path3 = Path.GetFullPath("file");//獲取包括該文件夾的全路徑C:\Users\Administrator\Documents\Visual Studio 2008\Projects\獲取各類文件路徑的操做\獲取各類文件路徑的操做\bin\Debug\file string[] dirs = Directory.GetDirectories(path3);//獲取包含子文件的文件夾 //string path2 = Path.GetFileName(dir);//根據上面循環獲取的文件夾,來獲取文件夾名字 string[] files = Directory.GetFiles(path3);//獲取該目錄下的全部文件(不包括擁有子文件的文件夾) //string[] files = Directory.GetFiles(path, "*.txt");///刷選當前目錄下的全部.txt的文件 //string path5 = Path.GetFileName(file);//根據上面循環獲取的文件,來獲取文件的名字
正則表達式:
判斷是否匹配:IsMatch(),字符串提取:Match();循環提取:Matchs();
\(1\)匹配(1),\.匹配.,\-匹配-
-?:表示-出現0次或一次
{3,}:表示匹配前面字符3次以上
(\.\w+)+:緊跟加號前面的字符出現一次或屢次,裏面的加號表示\w出現一次或屢次,外面的加號表示括號裏面的全面的內容出現一次或屢次(通常用於重複的出現的時候)
():一、表示優先級,一把用於表示裏面的字符出現屢次,二、分組提取字符串的功能
郵箱:通常驗證:\w+@\w+(\.\w+)+
較嚴謹的:[a-zA-Z0-9_\-]+@\w+(\.\w+)+
獲取網頁上的圖片:src=".+\.jpg"
// src="[^"]+\.jpg"
// @"src=""(.+?)""";//前面加了@符號,裏面用2個引號表示一個引號
// string regex = "src=\"(.+)?\"";//同上面
//匹配正確的郵政編碼(規定6爲數字)
#region MyRegion while (true) { Console.WriteLine("請輸入郵政編碼"); // string regex = @"^[0123456789]{6}$"; // string regex = @"^[0-9]{6}$"; string regex = @"^\d{6}$"; string res = Console.ReadLine(); if (Regex.IsMatch(res, regex)) { Console.WriteLine("郵編正確"); } else { Console.WriteLine("Error"); } } // 0 1 2 3 4 5678901234 string str = "你們好呀,hello, 20101010年10月10日是個好日子。恩,9494.吼吼!886."; string regex = @"\d+"; Match m = Regex.Match(str, regex);//match的用法 if (m.Success) { Console.WriteLine(m.Value); Console.WriteLine(m.Length); Console.WriteLine(m.Index); } MatchCollection ms = Regex.Matches(str, regex);//Regex.Matches的用法 foreach (Match m in ms) { if (m.Success) { Console.WriteLine(m.Value); Console.WriteLine(m.Length); Console.WriteLine(m.Index); } Console.WriteLine(); } string str = @"姓名:張三,年齡:25,個人性別:男; 姓名:李四,年齡:24,個人性別:男; 姓名:王五,年齡:27,個人性別:男; 姓名:趙倩,年齡:23,個人性別:女"; string temp = @"name='{0}', age={1}, gender='{2}'"; List<string> list = new List<string>(); string regex = @"姓名:(\w+),年齡:(\d+),個人性別:([男女])"; MatchCollection mc = Regex.Matches(str, regex); foreach (Match m in mc) { if (m.Success) { Console.WriteLine(m.Groups[0].Value);//整個匹配出來的結果就是Groups[0],姓名:張三,年齡:25,個人性別:男 Console.WriteLine(temp, m.Groups[1].Value, m.Groups[2].Value, m.Groups[3].Value);//regex表達式中,第一個()就是第一組Groups[1],第二個()就是第二組Groups[2],以此類推 } }
.:表示匹配任意字符
+:匹配前面字符或組的一個或多個
?:匹配前面字符或組的零個或一個
*:匹配前面字符或組的零個或多個
.+:匹配前面任意字符的一個或多個
提取字符串:用小括號()括起來的內容(\w+)@(\w+(\.\w+)+),想要提取誰就用括號括起來,
string str="<p>12313</p><p>21313</p>"
@「<p>(.+)</p>」貪婪模式,比配所有顯示<p>到結尾的</p>:<p>12313</p><p>21313</p>
@「<p>(.+?)</p>」取消貪婪模式,分組顯示,此時顯示2個:<p>12313</p>、<p>21313</p>
貪婪模式:左邊的優先級最高:從左向右,左邊第一個貪婪後,其餘後面的默認取一個值,當去掉(?)第一個貪婪模式後,第二個開始貪婪,後面的默認取一個值
Regex.Replace替換字符串
string html= Regex.Replace(strHtml,@"\s+"," ");//把多個連續的空格替換成一個 //string str = "aaaasssdddwwwaaadddffaaaeesadw"; //string regex=@"a+"; //str = Regex.Replace(str, regex, "a"); //Console.WriteLine(str); string str = "aaa 213 223 dw 2 d w"; Console.WriteLine(str); string regex = @"\s+"; str = Regex.Replace(str, regex, " "); Console.WriteLine(str); Console.ReadKey();
string email = @"huanjiansheng@163.com.cn"; string regex = @"(.+)(@.+)"; string str = Regex.Replace(email, regex, "*****$2");//當中的$2表示直接用前面的第二組(@.+)匹配到的內容 Console.WriteLine(str);
"******$2",替換前面的第二組
委託與事件的區別:
委託是類型,事件是對象(委託類型的對象)
事件只能在聲明的內部訪問,不能在外部訪問,外部不能觸發事件
事件不能像委託同樣使用=賦值,必須使用+=
在委託中最後使用=事會把前面的的多播委託給覆蓋掉
事件是特殊的委託變量
事件用來閹割委託事例
委託就是一個函數指針(指向一個函數的引用,表明一個方法)
事件就是一個消息機制的實現
委託:
委託是類型,委託就是一個函數指針(指向一個函數的引用,表明一個方法)
容許把方法做爲另外一個方法的參數進行進行傳遞
//注意此方法,它接受一個GreetingDelegate類型的方法做爲參數
private static void GreetPeople(string name, GreetingDelegate MakeGreeting) { MakeGreeting(name); } GreetPeople("Jimmy Zhang", EnglishGreeting);//EnglishGreeting是個方法,把方法做爲參數
可使用=和+=進行賦值,在委託中最後使用=事會把前面的的多播委託給覆蓋掉(因此須要使用事件)
委託表示了對其回調方法的簽名,能夠將方法看成參數進行傳遞,並根據傳入的方法來動態的改變方法調用。只要爲委託提供相同簽名的方法,就能夠與委託綁定。
委託,實現了類型安全的回調方法。在.NET中回調無處不在,因此委託也無處不在,事件模型創建在委託機制上,Lambda表達式本質上就是一種匿名委託。
委託是一個公開的對象,外部能夠對其進行任意的賦值,後面的程序員對其附加方法的時候,不知道前面是否有人爲該委託賦過值,可能此時用=號就把前面人註冊過的方法給覆蓋掉了。
GreetingDelegate delegate1;
delegate1 = EnglishGreeting; // 先給委託類型的變量賦值
delegate1 += ChineseGreeting; // 給此委託變量再綁定一個方法
// 將前後調用 EnglishGreeting 與 ChineseGreeting 方法
delegate1 ("Jimmy Zhang");
//注意這裏,第一次用的「=」,是賦值的語法;第二次,用的是「+=」,是綁定的語法。若是第一次就使用「+=」,將出現「使用了未賦值的局部變量」的編譯錯誤。
//既然給委託能夠綁定一個方法,那麼也應該有辦法取消對方法的綁定,很容易想到,這個語法是「-=」:
//使用委託能夠將多個方法綁定到同一個委託變量,當調用此變量時(這裏用「調用」這個詞,是由於此變量表明一個方法),能夠依次調用全部綁定的方法。
//委託是一個類,它定義了方法的類型,使得能夠將方法看成另外一個方法的參數來進行傳遞,這種將方法動態地賦給參數的作法,能夠避免在程序中大量使用If-Else(Switch)語句,同時使得程序具備更好的可擴展性。
//咱們在裏將事件聲明爲public,可是,實際上事件會被編譯成私有字段
//若是此時用「=」註冊,會編譯錯誤,由於它根本就不容許在事件類的外面以賦值的方式訪問,必須得用「+=」進行註冊。
//:MakeGreet事件確實是一個GreetingDelegate類型的委託,只不過無論是否是聲明爲public,它老是被聲明爲private。
//另外,它還有兩個方法,分別是add_MakeGreet和remove_MakeGreet,這兩個方法分別用於註冊委託類型的方法和取消註冊。
//delegate1 聲明爲 public 會怎樣?結果就是:在客戶端能夠對它進行隨意的賦值等操做,嚴重破壞對象的封裝性。
//delegate1 聲明爲 private會怎樣?結果就是:這簡直就是在搞笑。由於聲明委託的目的就是爲了把它暴露在類的客戶端進行方法的註冊,你把它聲明爲private了,
//客戶端對它根本就不可見,那它還有什麼用?
//使用委託能夠將多個方法綁定到同一個委託變量,當調用此變量時(這裏用「調用」這個詞,是由於此變量表明一個方法),能夠依次調用全部綁定的方法。
事件:
事件是對象(本質是一個提供了add和Remove方法的委託類型的對象)
事件是特殊的委託變量
事件用來閹割委託事例
事件其實就是爲了約束一個委託對象,對外只提供+=,-=操做
對委託對象進行的封裝,使改委託的對象變成private修飾,外部不能訪問,只能經過其提供的相似屬性的公有的add和remove方法進行訪問
//對事件的聲明實際是 聲明一個私有的委託變量
//聲明一個事件不過相似於聲明一個進行了封裝的委託類型的變量而已。
//:在類的內部,無論你聲明它是public仍是protected,它老是private的。在類的外部,註冊「+=」和註銷「-=」的訪問限定符與你在聲明事件時使用的訪問符相同。
委託與指針的區別:
委託是類型安全的,面向對象的,有豐富的內部數據結構
指針式不安全的代碼,面向過程的,是一個地址的指向
委託鏈:定義一個數組專門用來存儲多個方法的地址
方法指針:方法在內存中的地址,將方法的地址賦給了委託中method變量
DelegateFunc func = new DelegateFunc(Func)//實例化委託的時候必須傳一份方法進去,由於構造函數沒有采用0個參數的 func += Func1;//而後才能夠用+=進行註冊 func += Func3; func += Func4; DelegateFunc func;//定義一個委託變量 func = Func;//委託第一次賦值必須得用=號,若是第一次用+=,會報錯「使用了沒賦值的委託變量」 func += Func1; func += Func3; func += Func4; func = Func5;//此時若是使用=號進行賦值,會把前面的值給覆蓋掉
而事件:
Computer computer = new Computer(); CloseQQ closeqq = new CloseQQ(); computer.CloseAPP += closeqq.Close;//此時註冊方法只能用+=或-+,不能像委託同樣使用=號賦值,因此不存在會把前面的值覆蓋的可能性 CloseXunlei closexunlei = new CloseXunlei(); computer.CloseAPP += closexunlei.Close; CloseKP closekp = new CloseKP(); computer.CloseAPP += closekp.Close; computer.ShitDown();
程序集:
是net中獨有的概念
.exe和.dll都是程序集
程序集(Assembly)能夠看做是類庫和資源的打包
程序集中有哪些:
一、元數據(並不是源數據):程序中用到的全部方法、類、字段等一系列的關係表
二、IL代碼
三、資源文件
程序集的好處:
作爲類庫使用,提供對外的接口
減少類庫體積
如何添加程序集:
添加引用
全局程序緩存(GAC):自動加載程序集到運行的環境中的去,加到BUG目錄下面去
不能循環添加
//程序集不能夠相互引用
//調用同一個命名空間下的不一樣類和方法,把類庫寫成小的類庫程序集,每一個類庫包含特定的方法和做用,而後能夠
根據須要進行添加引用
//此時他們的命名空間都是:namespace 程序集
程序集.Class3 c3 = new 程序集.Class3();
c3.Find();
程序集.Class1 c1 = new 程序集.Class1();
c1.fuck();
程序集.Class2 c2 = new 程序集.Class2();
c2.Func();
反射:程序集中不添加引用,不更改原來的代碼,只經過在BUG目錄中添加Dll,就能夠自動調用剛添加的Dll裏面的方法,經過反射來調用程序集中的方法
Person p=new Person; Type type=p.GetType();//根據實例獲取當前對象的信息,獲取已知對象的信息 Type type=typeof(Person)//經過運算符來獲取(類型),當不知道對象類型的時候使用 而後經過type來獲取該對象的全部信息 MedodInfo[] methods=type.GetMethods();//獲取該對象的全部方法,而後for循環輸出methods[i].Name 獲取全部類型: Assembly asm=Assembly.LoadFile(@"c:\TestLIb.dll"); Type[] types=asm.GetTypes(); 獲取程序集中的全部的包含Public的類型 Type[] types=asm.GetExportedTypes();/只能獲取到public類型的 獲取某一個類型:獲取class1的相關信息,獲取class1的元數據 Type typeclass1=asm.GetType("TestLib.Class1"); 獲取class1中的全部方法 MedodInfo[] methods=typeclass1.GetMethods() 獲取某個方法 MethodInfo m=typeclass1.GetMethod("SayHi"); 建立一個class1類型的對象 object obj=Activator.CreateInstance(typeClass1); 調用該方法 m.Invoke(obj,null);//第一個參數爲對象,第二個參數是調用方法的參數,若是沒有則爲NULL 驗證person類是否是chinese類型的父類// 驗證是否是能夠把typechinese類型的對象賦值給typeperson類型 bool b=typeperson.IsAssignableFrom(typeChinese);//true 驗證是否是繼承接口//是否能夠將typechinese賦值給typeIxiuFu類型 bool b=typeIxiuFu.IsAssignableFrom(typechinese);//true //驗證obj是否是typechinese類型的對象 bool b=typechinese.IsInstanceOfType(obj);//true 驗證obj是否是typeperson類型的對象 bool b=typePerdon.IsInstanceOfType(obj);//true 驗證typechinese是否是typeperson類的子類 bool b=typechinese.IsSubclassOf(typeperson);//true; 驗證typechinese是否是接口類型typeIXiufFu的子類 IsSunclassOf不考慮接口,只考慮父子類的關係,子類只是實現接口,而不是屬於子父類的關係 bool b=typechinese.IsSunclassOf(typeIXiufFu);//false 只要不能被實例化都被認爲是抽象的 接口、抽象類、靜態類都不能被實例化,因此在這裏都被認爲是抽象的 bool b=typechinese.IsAbstract//false MemberInfo類,有不少子類,大多反射中用到類都是繼承它 獲取程序集成員的相關信息(類型,方法、事件、字段、屬性) ProperInfo:獲取屬性 設置值SetValue,獲取值:GetValue FileInfo//獲取字段 EventInfo:獲取事件
值類型與引用類型的區別:
值類型繼承自ValueType(注意:而System.ValueType又繼承自System.Object);而引用類型繼承自System.Object。
值類型變量包含其實例數據,每一個變量保存了其自己的數據拷貝(副本),所以在默認狀況下,值類型的參數傳遞不會影響參數自己;而引用類型變量保存了其數據的引用地址,所以以引用方式進行參數傳遞時會影響到參數自己,由於兩個變量會引用了內存中的同一塊地址。
值類型有兩種表示:裝箱與拆箱;引用類型只有裝箱一種形式。我會在下節以專門的篇幅來深刻討論這個話題。
典型的值類型爲:struct,enum以及大量的內置值類型;而能稱爲類的均可以說是引用類型。 struct和class主要的區別能夠參見個人拙做《第四回:後來居上:class和struct》來詳細瞭解,也是對值類型和引用類型在應用方面的有力補充。
值類型的內存不禁GC(垃圾回收,Gabage Collection)控制,做用域結束時,值類型會自行釋放,減小了託管堆的壓力,所以具備性能上的優點。例如,一般struct比class更高效;而引用類型的內存回收,由GC來完成,微軟甚至建議用戶最好不要自行釋放內存。
值類型是密封的(sealed),所以值類型不能做爲其餘任何類型的基類,可是能夠單繼承或者多繼承接口;而引用類型通常都有繼承性。
值類型不具備多態性;而引用類型有多態性。
值類型變量不可爲null值,值類型都會自行初始化爲0值;而引用類型變量默認狀況下,建立爲null值,表示沒有指向任何託管堆的引用地址。對值爲null的引用類型的任何操做,都會拋出NullReferenceException異常。
值類型有兩種狀態:裝箱和未裝箱,運行庫提供了全部值類型的已裝箱形式;而引用類型一般只有一種形式:裝箱。
注意點:
一、string類型是個特殊的引用類型,它繼承自System.Object確定是個引用類型,可是在應用表現上又凸現出值類型的特色:
簡單的說是因爲string的immutable特性,所以每次對string的改變都會在託管堆中產生一個新的string變量,上述string做爲參數傳遞時,實際上執行了s=s操做,在託管堆中會產生一個新的空間,並執行數據拷貝,因此纔有了相似於按值傳遞的結果。可是根據咱們的內存分析可知,string在本質上仍是一個引用類型,在參數傳遞時發生的仍是按址傳遞,不過因爲其特殊的恆定特性,在函數內部新建了一個string對象並完成初始化,可是函數外部取不到這個變化的結果,所以對外表現的特性就相似於按值傳遞
二、一般可使用Type.IsValueType來判斷一個變量的類型是否爲值類型
MyStructTester aStruct = new MyStructTester(); Type type = aStruct.GetType(); if (type.IsValueType) { Console.WriteLine("{0} belongs to value type.", aStruct.ToString()); }
三、操做符ref和out來標識值類型按引用類型方式傳遞,其中區別是:ref在參數傳遞以前必須初始化(傳入);而out(傳出)則在傳遞前沒必要初始化,且在傳遞時必須顯式賦值(函數內部傳出以前賦值)。
四、值類型與引用類型之間的轉換過程稱爲裝箱與拆箱
五、sizeof()運算符用於獲取值類型的大小,可是不適用於引用類型。
六、值類型使用new操做符完成初始化,例如:MyStruct aTest = new MyStruct(); 而單純的定義沒有完成初始化動做,此時對成員的引用將不能經過編譯,例如: MyStruct aTest; Console.WriteLine(aTest.X);
七、引用類型在性能上欠於值類型主要是由於如下幾個方面:引用類型變量要分配於託管堆上;內存釋放則由GC完成,形成必定的CG堆壓力;同時必須完成對其附加成員的內存分配過程;以及對象訪問問題。所以,.NET系統不能由純粹的引用類型來統治,性能和空間更加優越和易於管理的值類型有其一席之地,這樣咱們就不會由於一個簡單的byte類型而進行復雜的內存分配和釋放工做。Richter就稱值類型爲「輕量級」類型,簡直恰如其分,處理數據較小的狀況時,應該優先考慮值類型。
八、值類型都繼承自System.ValueType,而System.ValueType又繼承自System.Object,其主要區別是ValueType重寫了Equals方法,實現對值類型按照實例值比較而不是引用地址來比較
九、基元類型,是指編譯器直接支持的類型,其概念實際上是針對具體編程語言而言的,例如C#或者VB.NET,一般對應用.NET Framework定義的內置值類型。這是概念上的界限,不可混淆。例如:int對應於System.Int32,float對應於System.Single。
值類型的應用場合
MSDN中建議以類型的大小做爲選擇值類型或者引用類型的決定性因素。數據較小的場合,最好考慮以值類型來實現能夠改善系統性能;
結構簡單,沒必要多態的狀況下,值類型是較好的選擇;
類型的性質不表現出行爲時,沒必要以類來實現,那麼用以存儲數據爲主要目的的狀況下,值類型是優先的選擇;
參數傳遞時,值類型默認狀況下傳遞的是實例數據,而不是內存地址,所以數據傳遞狀況下的選擇,取決於函數內部的實現邏輯。值類型能夠有高效的內存支持,而且在不暴露內部結構的狀況下返回
實例數據的副本,從安全性上能夠考慮值類型,可是過多的值傳遞也會損傷性能的優化,應適當選擇;
值類型沒有繼承性,若是類型的選擇沒有子類繼承的必要,優先考慮值類型;
在可能會引發裝箱與拆箱操做的集合或者隊列中,值類型不是很好的選擇,由於會引發對值類型的裝箱操做,致使額外內存的分配,例如在Hashtable。關於這點我將在後續的主題中重點討論。
3.2 引用類型的應用場合
能夠簡單的說,引用類型是.NET世界的全值殺手,咱們能夠說.NET世界就是由類構成的,類是面向對象的基本概念,也是程序框架的基本要素,所以靈活的數據封裝特性使得引用類型成爲主流;
引用類型適用於結構複雜,有繼承、有多態,突出行爲的場合;
參數傳遞狀況也是考慮的必要因素;
4. 再論類型判等
類型的比較一般有Equals()、ReferenceEquals()和==/!=三種常見的方法,其中核心的方法是Equals。咱們知道Equals是System.Object提供的虛方法,用於比較兩個對象是否指向相同的引用地址,.NET Framework的不少類型都實現了對Equals方法的重寫,例如值類型的「始祖」System.ValueType就重載了Equal方法,以實現對實例數據的判等。所以,類型的判等也要從重寫或者重載Equals等不一樣的狀況具體分析,對值類型和引用類型判等,這三個方法各有區別,應多加註意。
4.1 值類型判等
Equals,System.ValueType重載了System.Object的Equals方法,用於實現對實例數據的判等。
ReferenceEquals,對值類型應用ReferenceEquals將永遠返回false。
==,未重載的==的值類型,將比較兩個值是否「按位」相等。
4.2 引用類型判等
Equals,主要有兩種方法,以下 public virtual bool Equals(object obj); public static bool Equals(object objA, object objB);
一種是虛方法,默認爲引用地址比較;而靜態方法,若是objA是與objB相同的實例,或者若是二者均爲空引用,或者若是objA.Equals(objB)返回true,則爲true;不然爲false。.NET的大部分類都重寫了Equals方法,所以判等的返回值要根據具體的重寫狀況決定。
ReferenceEquals,靜態方法,只能用於引用類型,用於比較兩個實例對象是否指向同一引用地址。
==,默認爲引用地址比較,一般進行實現了==的重載,未重載==的引用類型將比較兩個對象是否引用地址,等同於引用類型的Equals方法。所以,不少的.NET類實現了對==操做符的重載,例如System.String的==操做符就是比較兩個字符串是否相同。而==和equals方法的主要區別,在於多態表現上,==是被重載,而Equals是重寫。
有必要在自定義的類型中,實現對Equals和==的重寫或者重載,以提升性能和針對性分析。
5. 再論類型轉換
類型轉換是引發系統異常一個重要的因素之一,所以在有必要在這個主題裏作以簡單的總結,咱們不力求照顧全面,可是追去提綱挈領。常見的類型轉換包括:
隱式轉換:由低級類型項高級類型的轉換過程。主要包括:值類型的隱式轉換,主要是數值類型等基本類型的隱式轉換;引用類型的隱式轉換,主要是派生類向基類的轉換;值類型和引用類型的隱士轉換,主要指裝箱和拆箱轉換。
顯示轉換:也叫強制類型轉換。可是轉換過程不能保證數據的完整性,可能引發必定的精度損失或者引發不可知的異常發生。轉換的格式爲, (type)(變量、表達式)
例如:int a = (int)(b + 2.02);
值類型與引用類型的裝箱與拆箱是.NET中最重要的類型轉換,不恰當的轉換操做會引發性能的極大損耗,所以咱們將以專門的主題來討論。
以is和as操做符進行類型的安全轉換,詳見本人拙做《第一回:恩怨情仇:is和as》。
System.Convert類定義了完成基本類型轉換的便捷實現。
除了string之外的其餘類型都有Parse方法,用於將字符串類型轉換爲對應的基本類型;
使用explicit或者implicit進行用戶自定義類型轉換,主要給用戶提升自定義的類型轉換實現方式,以實現更有目的的轉換操做,轉換格式爲,
static 訪問修飾操做符 轉換修飾操做符 operator 類型(參數列表);
垃圾回收的簡單闡釋:
//實例定義及初始化 MyClass mc1 = new MyClass(); //聲明但不實體化 MyClass mc2; //拷貝引用,mc2和mc1指向同一託管地址 mc2 = mc1;
//定義另外一實例,並完成初始化 MyClass mc3 = new MyClass(); //引用拷貝,mc一、mc2指向了新的託管地址 //那麼原來的地址成爲GC回收的對象,在 mc1 = mc3; mc2 = mc3; #endregion
}
GC執行時,會遍歷全部的託管堆對象,按照必定的遞歸遍歷算法找出全部的可達對象和不可訪問對象,顯然本示例中的託管堆A對象沒有被任何引用訪問,屬於不可訪問對象,將被列入執行垃圾收集的目標。
參數基礎論:
簡單的來講,參數實現了不一樣方法間的數據傳遞,也就是信息交換。Thinking in Java的做者有過一句名言:一切皆爲對象。在.NET語言中也是如此,一切數據都最終抽象於類中封裝,所以參數通常用於方法間的數據傳遞。例如典型的Main入口函數就有一個string數組參數,args是函數命令行參數。一般參數按照調用方式能夠分爲:形參和實參。形參就是被調用方法的參數,而實參就是調用方法的參數。例如: using System; public class Arguments { public static void Main(string [] args) { string myString = "This is your argument."; //myString是實際參數 ShowString(myString); } private void ShowString(string astr) { Console.WriteLine(astr); } }
由上例能夠得出如下幾個關於參數的基本語法:
形參和實參必須類型、個數與順序對應匹配;
參數能夠爲空;
解析Main(string [] args),Main函數的參數能夠爲空,也能夠爲string數組類,其做用是接受命令行參數,例如在命令行下運行程序時,args提供了輸入命令行參數的入口。
另外,值得一提的是,雖然CLR支持參數默認值,可是C#中卻不能設置參數默認值,這一點讓我很鬱悶,不知爲什麼?不過能夠經過重載來變相實現
泛型類型參數:
泛型類型參數,能夠是靜態的,例如MyGeneric<int>;也能夠是動態的,此時它其實就是一個佔位符,例如MyGeneric<T>中的T能夠是任何類型的變量,在運行期動態替換爲相應的類型參數。泛型類型參數通常也以T開頭來命名。
可變數目參數:
通常來講參數個數都是固定的,定義爲集羣類型的參數能夠實現可變數目參數的目的,可是.NET提供了更靈活的機制來實現可變數目參數,這就是使用param修飾符。可變數目參數的好處就是在某些狀況下能夠方便的提供對於參數個數不肯定狀況的實現,例如計算任意數字的加權和,鏈接任意字符串爲一個字符串等。咱們以一個簡單的示例來展開對這個問題的論述,
param關鍵字的實質是:param是定製特性ParamArrayAttribute的縮寫(關於定製特性的詳細論述請參見第三回:歷史糾葛:特性和屬性),該特性用於指示編譯器的執行過程大概能夠簡化爲:編譯器檢查到方法調用時,首先調用不包含ParamArrayAttribute特性的方法,若是存在這種方法就施行調用,若是不存在才調用包含ParamArrayAttribute特性的方法,同時應用方法中的元素來填充一個數組,同時將該數組做爲參數傳入調用的方法體。總之就是param就是提示編譯器實現對參數進行數組封裝,將可變數目的控制由編譯器來完成,咱們能夠很方便的從上述示例中獲得啓示。例如:
static void ShowAgeSum(string team, params int[] ages){...}
實質上是這樣子:
static void ShowAgeSum(string team, [ParamArrayAttribute] int[] ages){...}
param修飾的參數必須爲一維數組,事實上一般就是以羣集方式來實現多個或者任意多個參數的控制的,因此數組是最簡單的選擇;
param修飾的參數數組,但是是任何類型。所以,若是須要接受任何類型的參數時,只要設置數組類型爲object便可;
param必須在參數列表的最後一個,而且只能使用一次。
參數傳遞:值類型傳遞、引用類型傳遞
參數傳遞根據參數類型分爲按值傳遞和按引用傳遞。
值類型傳遞:(值類型參數的按值傳遞和引用類型參數的按值傳遞)
一、默認狀況下都是按值傳遞的。按值傳遞主要包括值類型參數的按值傳遞和引用類型參數的按值傳遞。值類型實例傳遞的是該值類型實例的一個拷貝,所以被調用方法操做的是屬於本身自己的實例拷貝,所以不影響原來調用方法中的實例值。
二、引用類型參數的按值傳遞
當傳遞的參數爲引用類型時,傳遞和操做的是指向對象的引用,這意味着方法操做能夠改變原來的對象,可是值得思考的是該引用或者說指針自己仍是按值傳遞的。
咱們從基本的理解入手來了解引用類型參數按值傳遞的本質所在,簡單的說對象做爲參數傳遞時,執行的是對對象地址的拷貝,操做的是該拷貝地址。這在本質上和值類型參數按值傳遞是相同的,都是按值傳遞。不一樣的是值類型的「值」爲類型實例,而引用類型的「值」爲引用地址。所以,若是參數爲引用類型時,在調用方代碼中,能夠改變引用的指向, 從而使得原對象的指向發生改。
按值傳遞的實質的是傳遞值,不一樣的是這個值在值類型和引用類型的表現是不一樣的:參數爲值類型時,「值」爲實例自己,所以傳遞的是實例拷貝,不會對原來的實例產生影響;參數爲引用類型時,「值」爲對象引用,所以傳遞的是引用地址拷貝,會改變原來對象的引用指向
class Program { static void Main(string[] args) { Myclass my = new Myclass(); Console.WriteLine(my.str);//方法調用以前 輸出:我是Myclass Change(my);//方法中 輸出:我在Change方法中 Console.WriteLine(my.str);//方法調用以後 輸出:我在Change方法中,原來的值被改變了。 Console.ReadKey(); } public static void Change(Myclass mc) { mc.str = "我在Change方法中";//若是參數爲引用類型時,在調用方代碼中,能夠改變引用的指向, 從而使得原對象的指向發生改變 Console.WriteLine(mc.str); } } public class Myclass { public string str = "我是Myclass"; }
引用類型參數的按值傳遞和按引用傳遞的區別:
引用類型參數的按值傳遞,傳遞的是參數自己的值,也就是上面提到的對象的引用;
按引用傳遞,傳遞的不是參數自己的值,而是參數的地址。若是參數爲值類型,則傳遞的是該值類型的地址;若是參數爲引用類型,則傳遞的是對象引用的地址。
值類型傳遞:按值傳遞的實質的是傳遞值,不一樣的是這個值在值類型和引用類型的表現是不一樣的:參數爲值類型時,「值」爲實例自己,所以傳遞的是實例拷貝,不會對原來的實例產生影響;參數爲引用類型時,「值」爲對象引用,所以傳遞的是引用地址拷貝,會改變原來對象的引用指向