劉鐵猛《C#語(yǔ)言入門詳解》全集

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace 劉鐵猛課程練習(xí)
{
??internal class 參數(shù)?
??{
????//傳值參數(shù)(也稱值參數(shù)):指在聲明的時(shí)候不帶任何修飾符的參數(shù),這種參數(shù)的本質(zhì)就是作用域?yàn)楫?dāng)前方法的一個(gè)局部變量,而且它的初始值為調(diào)用這個(gè)方法的時(shí)候賦給它的實(shí)參的值。
????//輸出參數(shù):指在聲明時(shí)使用out關(guān)鍵字進(jìn)行修飾的參數(shù)
????//引用參數(shù):指在聲明時(shí)使用ref關(guān)鍵字進(jìn)行修飾的參數(shù)
????//數(shù)組參數(shù):指在聲明時(shí)使用Params關(guān)鍵字進(jìn)行修飾的參數(shù)
????//具名參數(shù):值在調(diào)用方法的時(shí)候,傳進(jìn)去的參數(shù)是帶有名字的參數(shù)。
????//可選參數(shù):具有默認(rèn)值的參數(shù)
????//拓展方法(使用this參數(shù))
????static void Main()
????{
??????//傳值參數(shù)---------------------------------------------------------------
??????//傳值參數(shù)—值類型
??????int y = 100;
??????傳值參數(shù)_值類型(y);?//輸出101,副本的值經(jīng)過運(yùn)算發(fā)生改變
??????Console.WriteLine(y);?//輸出100,原本的值不會(huì)受到影響。
??????//引用類型—并且新創(chuàng)建對(duì)象
??????Student5 stu = new Student5() { Name = "Tim" };
??????傳值參數(shù)_引用類型并且新創(chuàng)建對(duì)象(stu); //輸出:46104728,Tim
??????Console.WriteLine($"{stu.GetHashCode()},{stu.Name}"); //輸出:12289376,Tim
??????//可以看出輸入Name的值都被賦予"Tim",但是其HashCode并不相同,這是兩個(gè)獨(dú)立存在的對(duì)象。
??????//引用類型—只操作對(duì)象不新創(chuàng)建對(duì)象
??????Student6 stu2 = new Student6() { Name = "Tim" };
??????傳值參數(shù)_引用類型只操作對(duì)象不新創(chuàng)建對(duì)象(stu2);?//輸出:43495525,Tim
??????Console.WriteLine($"{stu2.GetHashCode()},{stu2.Name}");?//輸出:43495525,Tim
??????//由于變量和參數(shù)指向的是同一個(gè)對(duì)象,通過參數(shù)修改對(duì)象的值后,當(dāng)嘗試用變量再去訪問這個(gè)對(duì)象的時(shí)候
??????//拿到的也是更新之后的值。 通過HashCode可以看出,它們確實(shí)是同一個(gè)對(duì)象。
??????//引用參數(shù)---------------------------------------------------------------
??????//引用參數(shù)—值類型
??????int y1 = 1;
??????引用參數(shù)_值類型(ref y1);?//輸出:101
??????Console.WriteLine(y1);??//輸出:101
??????//因?yàn)榉椒ǖ膮?shù)它指向的地址和方法外部變量指向的內(nèi)存地址是同一個(gè)地址,這樣當(dāng)我們?cè)诜椒▋?nèi)部改變了這個(gè)地址
??????//上的值之后,方法外部的變量它所指向的內(nèi)存地址當(dāng)中的值就已經(jīng)改變了。這時(shí)候當(dāng)我們通過外部的變量去訪問內(nèi)存地址當(dāng)中
??????//的值的時(shí)候,拿到的就是更新之后的值。所以y1為101.
??????//引用參數(shù)—引用類型并創(chuàng)建新對(duì)象
??????Student7 outerStu = new Student7() { Name = "Tim" };
??????Console.WriteLine($"{outerStu.GetHashCode()},Name={outerStu.Name}");?//輸出:55915408,Name = Tim
??????Console.WriteLine("————————————");
??????引用參數(shù)_引用類型并創(chuàng)建新對(duì)象(ref outerStu);?????????????//輸出:33476626,Name = Tom
??????Console.WriteLine($"{outerStu.GetHashCode()},Name={outerStu.Name}\n");?//輸出:33476626,Name = Tom
??????//55行對(duì)52行的outerStu類型新建了一個(gè)對(duì)象并初始化了Name屬性的值,再將其應(yīng)用到原本的外部變量outerStu上,
??????//所以53行與56行會(huì)發(fā)生改變。因?yàn)樾陆藢?duì)象。用新對(duì)象去改變的原來(lái)的外部變量。
??????//引用參數(shù)—引用類型不創(chuàng)建新對(duì)象只改變對(duì)象值
??????Student8 outerStu2 = new Student8() { Name = "Tim" };
??????Console.WriteLine($"{outerStu2.GetHashCode()},Name={outerStu2.Name}");?//輸出:32854180,Name=Tim
??????Console.WriteLine("————————————");
??????引用參數(shù)_引用類型不創(chuàng)建新對(duì)象只改變對(duì)象值(ref outerStu2);????????//輸出:32854180,Name=Tim?????
??????Console.WriteLine($"{outerStu2.GetHashCode()},Name={outerStu2.Name}\n");?//輸出:32854180,Name=Tim
??????//由此可見一直在操作同一個(gè)對(duì)象。
??????//因?yàn)?11行的方法:static void 引用參數(shù)_引用類型不創(chuàng)建新對(duì)象只改變對(duì)象值(ref Student8 stu)中stu這個(gè)參數(shù)和
??????//61行outerStu2這個(gè)變量它們所指向的內(nèi)存地址就是同一個(gè)內(nèi)存地址,而在這個(gè)內(nèi)存地址里所存儲(chǔ)的就是對(duì)象在堆內(nèi)存中的地址。
??????//輸出參數(shù)---------------------------------------------------------------
??????輸出參數(shù)的使用示例();
??????//輸出參數(shù)—值類型
??????double? x = 100;
??????bool b = 輸出參數(shù)_值類型("qq", out x);
??????if (b)
??????{
????????Console.WriteLine(x + 1);?//如果方法第一個(gè)參數(shù)正確,例如為"200",由于輸出參數(shù)的特性,那么x的值會(huì)被覆蓋為201。再打印輸出為202。
??????}
??????else
??????{
????????Console.WriteLine($"是否有值:{x.HasValue}\n");?//輸出False,為空值。
????????//即使在方法外定義過x的值=100,但是通過異常捕獲流程賦予了x=null空值,由于輸出參數(shù)的特性,外部變量被覆蓋為空。
??????}
??????//輸出參數(shù)—引用類型
??????Student9 stu3 = null;
??????bool b2 = 輸出參數(shù)_引用類型("Tim", 34, out stu3); //如果符合條件,把新創(chuàng)建對(duì)象完成的改動(dòng)(其中已初始化好Age和Name的值)向外輸出到stu3這個(gè)示例。
??????if (b2)
??????{
????????Console.WriteLine($"Student:{stu3.Name},Age is:{stu3.Age}\n");
??????}
??????//數(shù)組參數(shù)---------------------------------------------------------------
??????//int[] myArray = new int[] { 1, 5, 5, 1, 3 };
??????//int result = 數(shù)組參數(shù)的使用示例(myArray);?//在方法中加上或者不加params關(guān)鍵字,輸出結(jié)果絲毫不受影響。
??????int result = 數(shù)組參數(shù)的使用示例(1, 5, 5, 1, 3);
??????//與上一行result的輸出結(jié)果一樣,這就意味著使用params數(shù)組參數(shù),并不需要提前聲明一個(gè)數(shù)組,而只需要把數(shù)組的元素一個(gè)一個(gè)列出來(lái)就可以了,
??????//因?yàn)楫?dāng)編譯器發(fā)現(xiàn)括號(hào)中的參數(shù)是一個(gè)params參數(shù)的時(shí)候,會(huì)自動(dòng)聲明一個(gè)數(shù)組,然后把給出的值放入自動(dòng)聲明的數(shù)組再傳進(jìn)方法當(dāng)中。
??????Console.WriteLine("{0}\n", result); //輸出result的值,這種WriteLine輸出形式也使用了params參數(shù)的重載。
??????string str = "Tim,Tom,Kity,Pig";
??????string[] result2 = str.Split(','); //Split方法用于分割字符串,也是帶有params參數(shù)的方法。
??????foreach (var item in result2)
??????{
????????Console.WriteLine(item);
??????}
??????Console.WriteLine(); //單純?yōu)榱烁袷角逦?/p>
??????//具名參數(shù)--------------------------------------------------------------
??????//這是不具名的調(diào)用
??????具名參數(shù)("Tom", 25); //因?yàn)榈谝粋€(gè)參數(shù)是string類型,第二個(gè)參數(shù)是int類型,需按指定的數(shù)據(jù)類型來(lái)填寫,這種方式為不具名的調(diào)用。
??????//這是具名的調(diào)用
??????具名參數(shù)(Name: "Tom", age: 25);
??????具名參數(shù)(age: 25, Name: "Tom"); //順序可以不一樣,作用完全相同。
??????//這樣的好處是讓代碼看起來(lái)更清晰,能夠看出這個(gè)參數(shù)是干什么的,而且具名調(diào)用時(shí)順序不再受約束。
??????Console.WriteLine(); //單純?yōu)榱烁袷角逦?/p>
??????//可選參數(shù)---------------------------------------------------------------
??????可選參數(shù)(); //可以不寫參數(shù),因?yàn)槁暶鞣椒〞r(shí)已賦予參數(shù)默認(rèn)值,Name="Tim",Age=25,輸出這兩個(gè)值。
??????//拓展方法(使用this參數(shù))-------------------------------------------------------------- 位于Student9類的下面,在DoubleExtention類中。
??????double p = 3.1415926;
??????double m = p.Round(4);
??????//原本定義的Round方法有兩個(gè)參數(shù),但此時(shí)可以發(fā)現(xiàn)Round方法只接收一個(gè)參數(shù),是因?yàn)?前面的p就是Round方法的第一個(gè)參數(shù)
??????//現(xiàn)在只需要在Round方法中輸入一個(gè)參數(shù),也就是代表第二個(gè)參數(shù)就可以了。
????}
????//傳值參數(shù)---------------------------------------------------------------
????static void 傳值參數(shù)_值類型(int x)
????//參數(shù)x是傳值參數(shù),調(diào)用方法傳進(jìn)來(lái)的值在方法體內(nèi)部有一個(gè)副本,方法體內(nèi)的語(yǔ)句操作改變的是這個(gè)副本的值,
????//并不會(huì)影響方法體外部變量的值,所以無(wú)論如何操作這個(gè)值,原本的值都不會(huì)受到影響。
????{
??????x = x + 1;
??????Console.WriteLine(x);
????}
????static void 傳值參數(shù)_引用類型并且新創(chuàng)建對(duì)象(Student5 stu)
????{
??????//值參數(shù)創(chuàng)建變量的副本,對(duì)值參數(shù)的操作永遠(yuǎn)不影響變量的值。
??????stu = new Student5() { Name = "Tim" }; //新創(chuàng)建了一個(gè)對(duì)象,相當(dāng)于生成了一個(gè)副本,對(duì)副本的改動(dòng)不會(huì)影響到原本Student5類的實(shí)例stu的Name所賦予的值。
??????Console.WriteLine($"{stu.GetHashCode()},{stu.Name}");
??????//GetHashCode()隸屬于Object類的方法,無(wú)論什么數(shù)據(jù)類型都有此方法,調(diào)用此方法時(shí),
??????//得到的是一個(gè)用來(lái)代表對(duì)象的唯一值,所以每個(gè)對(duì)象的HashCode都不一樣。
????}
????static void 傳值參數(shù)_引用類型只操作對(duì)象不新創(chuàng)建對(duì)象(Student6 stu)
????{
??????//參數(shù)的值和傳進(jìn)來(lái)的值都是對(duì)象在堆內(nèi)存上的地址
??????stu.Name = "Tim";
??????Console.WriteLine($"{stu.GetHashCode()},{stu.Name}");
????}
????//引用參數(shù)---------------------------------------------------------------
????//引用參數(shù)不會(huì)為傳進(jìn)來(lái)的實(shí)際參數(shù)創(chuàng)建副本,也就是說(shuō)引用參數(shù)直接指向傳進(jìn)來(lái)的實(shí)際參數(shù)所指向的內(nèi)存地址。
????//正如文檔中說(shuō):引用參數(shù)并不創(chuàng)建新的存儲(chǔ)位置,引用參數(shù)表示的存儲(chǔ)位置就是在方法調(diào)用中作為實(shí)參給出的那個(gè)變量所表示的存儲(chǔ)位置。
????//注意:當(dāng)在調(diào)用帶有引用參數(shù)的方法的時(shí)候,作為實(shí)際參數(shù)傳進(jìn)來(lái)的變量之前必須要有明確的賦值。
????//而且在調(diào)用方法的時(shí)候,參數(shù)前面也要叫上ref關(guān)鍵字。 即 調(diào)用的引用參數(shù)方法(ref x);
????//使用ref修飾符顯示指出----此方法的副作用是改變實(shí)際參數(shù)的值。
????static void 引用參數(shù)_值類型(ref int x)
????{
??????x = x + 100;
??????Console.WriteLine(x);
????}
????static void 引用參數(shù)_引用類型并創(chuàng)建新對(duì)象(ref Student7 stu)
????{
??????stu = new Student7() { Name = "Tom" };
??????Console.WriteLine($"{stu.GetHashCode()},Name={stu.Name}");
????}
????static void 引用參數(shù)_引用類型不創(chuàng)建新對(duì)象只改變對(duì)象值(ref Student8 stu)
????{
??????stu.Name = "Tom";
??????Console.WriteLine($"{stu.GetHashCode()},Name={stu.Name}");
????}
????//輸出參數(shù)---------------------------------------------------------------
????//可通過使用輸出參數(shù)來(lái)獲得除返回值之外的額外的輸出。也就是說(shuō)拿輸出參數(shù)當(dāng)作一個(gè)值的輸出。
????//輸出參數(shù)不為傳進(jìn)來(lái)的參數(shù)創(chuàng)建副本。也就是說(shuō)輸出參數(shù)和傳進(jìn)來(lái)的實(shí)參指向的是同一個(gè)內(nèi)存地址。
????//C#并不要求輸出參數(shù)在傳入方法之前要明確賦值,但是因?yàn)檩敵鰠?shù)把值從方法體內(nèi)輸出到方法之外,
????//所以要求在方法體內(nèi)要求有明確的賦值。
????//從語(yǔ)義上來(lái)講——ref是為了“改變”,out是為了“輸出”。
????static void 輸出參數(shù)的使用示例()
????{
??????Console.WriteLine("請(qǐng)輸入第一個(gè)數(shù)字");
??????string arg1 = Console.ReadLine();
??????double x = 0;
??????bool b1 = double.TryParse(arg1, out x); //TryParse方法返回的是一個(gè)bool類型的返回值,這里對(duì)x進(jìn)行額外輸出,輸出值為輸入值。
??????if (b1 == false)
??????{
????????Console.WriteLine("輸入錯(cuò)誤");
????????return;
??????}
??????Console.WriteLine("請(qǐng)輸入第一個(gè)數(shù)字");
??????string arg2 = Console.ReadLine();
??????double y = 0;
??????bool b2 = double.TryParse(arg2, out y);
??????if (b2 == false)
??????{
????????Console.WriteLine("輸入錯(cuò)誤");
????????return;
??????}
??????double z = x + y;
??????Console.WriteLine($"{x}+{y}={z}\n");
????}
????//輸出參數(shù)—值類型
????static bool 輸出參數(shù)_值類型(string input, out double? result)
????{
??????try
??????{
????????result = double.Parse(input) + 1;
????????return true;
??????}
??????catch //沒有括號(hào)代表不管什么異常,都會(huì)抓住
??????{
????????result = null;
????????return false;
??????}
????}
????//輸出參數(shù)—引用類型
????/// <summary>
????/// <para>賦值前:</para>
????/// 把引用類型的變量以輸出參數(shù)的形式傳進(jìn)方法,此時(shí)輸出參數(shù)不為變量創(chuàng)建副本,也就是說(shuō)參數(shù)和變量它們指向同一個(gè)地址,
????/// 輸出參數(shù)可以有初始值,也可以沒有初始值,對(duì)于一個(gè)引用類型的變量來(lái)說(shuō)如果它有初始值,指的就是這個(gè)變量它引用著堆上的
????/// 一個(gè)對(duì)象,如果沒有初始值說(shuō)明它沒有引用任何對(duì)象。
????///<para>賦值后:</para>
????///大多數(shù)情況下,為一個(gè)引用變量進(jìn)行賦值,賦值符號(hào)的右邊都是new操作符的表達(dá)式,賦值后會(huì)把new操作符創(chuàng)建出來(lái)的對(duì)象它的地址
????///交給輸出參數(shù),而輸出參數(shù)和變量指向的是同一個(gè)地址,現(xiàn)在這個(gè)地址里所存儲(chǔ)的就是新創(chuàng)建的對(duì)象在堆內(nèi)存上的地址。
????///<para>從代碼上來(lái)看,我們?cè)诜椒w內(nèi)把一個(gè)新對(duì)象交由輸出參數(shù)進(jìn)行引用,實(shí)際效果是方法外的變量也引用上了新創(chuàng)建的對(duì)象。</para>
????/// </summary>
????static bool 輸出參數(shù)_引用類型(string stuName, int stuAge, out Student9 result)
????{
??????result = null;
??????if (string.IsNullOrEmpty(stuName) | stuAge < 20 | stuAge > 80)
??????{
????????return false;
??????}
??????result = new Student9() { Name = stuName, Age = stuAge };
??????return true;
????}
????//數(shù)組參數(shù)---------------------------------------------------------------
????/// <summary>
????/// 注意:在方法聲明中的 params 關(guān)鍵字之后不允許任何其他參數(shù)(即數(shù)組參數(shù)必須放在參數(shù)的最后),并且在方法聲明中只允許一個(gè) params 關(guān)鍵字。
????/// </summary>
????/// <param name="intArray"></param>
????/// <returns></returns>
????static int 數(shù)組參數(shù)的使用示例(params int[] intArray)
????{
??????int sum = 0;
??????foreach (var item in intArray)
??????{
????????sum += item;
??????}
??????return sum;
????}
????//具名參數(shù)--------------------------------------------------------------
????static void 具名參數(shù)(string Name, int age)
????{
??????Console.WriteLine($"Name:{Name},Age:{age}");
????}
????//可選參數(shù)---------------------------------------------------------------
????/// <summary>
????/// 可選參數(shù)指當(dāng)在調(diào)用一個(gè)方法的時(shí)候,方法的參數(shù)可寫也可不寫
????/// <para>可不寫的原因是因?yàn)樵诼暶鞣椒ǖ臅r(shí)候,給參數(shù)被賦予了默認(rèn)值,對(duì)于帶有默認(rèn)值的參數(shù),在調(diào)用方法的時(shí)候不寫這個(gè)參數(shù),
????/// 那么這個(gè)參數(shù)自動(dòng)獲得聲明時(shí)的默認(rèn)值。</para>
????/// </summary>
????static void 可選參數(shù)(string Name = "Tim", int age = 25)
????{
??????Console.WriteLine($"Name:{Name},Age:{age}");
????}
??}
??class Student5
??{
????public string Name { get; set; }
??}
??class Student6
??{
????public string Name { get; set; }
??}
??class Student7
??{
????public string Name { get; set; }
??}
??class Student8
??{
????public string Name { get; set; }
??}
??class Student9
??{
????public int Age { get; set; }
????public string Name { get; set; }
??}
??//拓展方法(使用this參數(shù))--------------------------------------------------------------
??//當(dāng)我們無(wú)法對(duì)一個(gè)類型的源碼進(jìn)行修改的時(shí)候,可以使用拓展方法為這種目標(biāo)數(shù)據(jù)類型來(lái)追加方法。
??//注意:
??//拓展方法必需是共有的,靜態(tài)的,即被public static所修飾。
??//this參數(shù)必須是參數(shù)列表中的第一個(gè)
??//拓展方法必須放在一個(gè)靜態(tài)類里
??//約定俗成當(dāng)打算拓展一個(gè)XXX的數(shù)據(jù)類型的時(shí)候,這個(gè)類應(yīng)該叫XXXExtension。
??/// <summary>
??/// 拓展方法所隸屬的類,要使用Extension來(lái)結(jié)尾,Extention翻譯為—拓展名。
??/// </summary>
??static class DoubleExtention
??{
????public static double Round(this double input,int digits)
????{
??????double result = Math.Round(input, digits);
??????return result;
????}
??}
}