C#基础操作符详解
本節(jié)內(nèi)容:
1.操作符概覽;
2.操作符的本質(zhì);
3.操作符與運算順序
4.操作符詳解。
?
1.操作符概覽:
?
?
?
操作符(Operator)也譯為”運算符”
操作符是用來操作數(shù)據(jù)的,被操作符操作的數(shù)據(jù)稱為操作數(shù)(Operand)
表格從上往下優(yōu)先級遞減,同一行運算符的優(yōu)先級一樣一般按從左到右算,
“=”賦值操作符,是先運算右邊的值再運算左邊的值,所以是最后運算的。
?
2.操作符的本質(zhì)
①操作符的本質(zhì)是函數(shù)(即算法)的”簡記法”
假如沒有發(fā)明”+”只有Add函數(shù),算式3+4+5將可以寫成Add(Add(3,4),5)
假設(shè)沒有發(fā)明”*”只有Mul函數(shù),那么算式3+4*5將只能寫成Add(3,Mul(4,5))
可見有操作符可讀性更強(qiáng)。
②操作符不能脫離與它關(guān)聯(lián)的數(shù)據(jù)類型(比如double數(shù)據(jù)類型的除法與int類型的除法相同數(shù)據(jù)結(jié)果不同)
可以說操作符就是與固定數(shù)據(jù)相關(guān)聯(lián)的一套基本算法的簡記法。
示例:為自定義的數(shù)據(jù)類型創(chuàng)建操作符。(格式為把方法名字改為”operator ?想要定義的操作符”如:”operator +”)如下例子進(jìn)一步說明了C#里面的操作符就是方法,也就是函數(shù)的一個簡記法。
?
復(fù)制代碼
class Person{public string Name;//public static List<Person>GetMary(Person p1, Person p2)(一般方法自定義操作符之前)public static List<Person>operator +(Person p1, Person p2){List<Person> people = new List<Person>();people.Add(p1);people.Add(p2);for (int i = 0; i < 11; i++){Person child = new Person();child.Name = p1.Name + "&" + p2.Name + "'s child";people.Add(child);}return people;}}?
?
?
?
3.操作符與運算順序
①操作符的優(yōu)先級
可以使用圓括號提高被括起來的表達(dá)式的優(yōu)先級。
圓括號可以嵌套。
不像數(shù)學(xué)里面有方括號和花括號,在C#語法中”[]”與”{}”有專門的用途。
②同優(yōu)先級操作符的運算順序
除了帶有賦值功能的操作符,同優(yōu)先級操作符都是有左到右進(jìn)行運算,
帶有賦值功能的操作符的運算順序是由右到左(比如賦值運算符”=”),
與數(shù)學(xué)運算不同,計算機(jī)語言的同優(yōu)先級運算沒有”結(jié)合率”:
3+4+5只能理解為Add(Add(3,4),5)不能理解為Add(3,Add(4,5)。
?
4.1基本操作符
①(成員訪問操作符)”.”操作符(上表中寫為X.Y):四種功能;
*訪問命名空間當(dāng)中的子集命名空間;
*訪問名稱空間當(dāng)中的類型;
*訪問類型的靜態(tài)成員(靜態(tài)成員隸屬于類本身故用類可以訪問,而用類的對象不能訪問類的靜態(tài)成員);
*訪問對象的成員(包括數(shù)據(jù)成員和方法);
②方法調(diào)用操作符”()”即方法后面跟著的那對圓括號(上表寫為f(x))。
調(diào)用方法一定要加圓括號,但是:
Action myAction = new Action(c.PrintHello);//把PrintHello方法交給委托對象myAction管理
??myAction();//這樣在委托對象后面加個圓括號就相當(dāng)于調(diào)用了被它管理的方法了,
這個時候PrintHello方法可以不帶圓括號。
③元素訪問操作符”[]”
???int[] myIntArray = new int[13];//創(chuàng)建int數(shù)組的實例13個元素
???int[] myIntArray2 = new int[]{1,2,3,4,5};//也可以在后面加花括號輸入相應(yīng)值,這對或括號叫做"初始化器",[]里不寫數(shù)組大小會根據(jù)初始化器自動賦值...
???myIntArray[0]=2;//訪問的是第一個數(shù)組元素,訪問數(shù)組元素,[]里寫的是偏移量,從0開始。
?
Dictionary<string, Student> stuDic = new Dictionary<string, Student>();//一個類名后面,跟著一個尖括號表示這個類是泛型
????????????//泛型是不完整的類如Dictionary<string, Student>在尖括號里要說明索引的類型(string)與值的類型(Student)(順帶一提Dictionary是一個字典類型)
?
*總結(jié)元素訪問操作符”[]”里面放的是索引里面不一定是整數(shù),如以下舉例。
復(fù)制代碼
1 class Program2 {3 static void Main(string[] args)4 {5 Dictionary<string, Student> stuDic = new Dictionary<string, Student>();//一個類名后面,跟著一個尖括號表示這個類是泛型6 //泛型是不完整的類如Dictionary<string, Student>在尖括號里要說明索引的類型(string)與值的類型(Student)(順帶一提Dictionary是一個字典類型)7 for (int i = 1; i < 100; i++)8 {9 Student stu = new Student(); 10 stu.Name = "s_" + i.ToString(); 11 stu.Score = 100+i; 12 stuDic.Add(stu.Name, stu);//把stu放進(jìn)字典里面,所以為stu.Name,值為stu 13 } 14 Student number6 = stuDic["s_6"];//說明了[]里不一定是整數(shù),而一定是索引 15 Console.WriteLine(number6.Score); 16 } 17 } 18 class Student 19 { 20 public string Name; 21 public int Score; 22 }?
④x--與x++:叫做后置的加加和后置的減減:
Int x=100; int y=x++;結(jié)果為x=101;y=100;因為x++是先賦值再進(jìn)行自增;
--x與++x:叫做前置的加加和前置的減減:先進(jìn)行自增或自減后進(jìn)行賦值。
?
⑤typeof()操作符和default()操作符
*typeof操作符的作用為查看變量的種類:
Type t = typeof(int);
????????????Console.WriteLine(t.Namespace);
????????????Console.WriteLine(t.FullName);
????????????Console.WriteLine(t.Name);
*Default操作符使操作數(shù)取默認(rèn)值:數(shù)值型為0,引用型為null,
??int x=default(int);//default操作的類型為結(jié)構(gòu)體類型即數(shù)值類型時就返回內(nèi)存塊當(dāng)中為0的值:
??Console.WriteLine(x);
輸出為0;
??Form myForm = default(Form);//default操作數(shù)的類型為引用類型時就返回內(nèi)存塊當(dāng)中為0的值即為null
??Console.WriteLine(myForm==null);
輸出為true;
當(dāng)為枚舉型enum時:?Level level=default(Level);
???????????? Console.WriteLine(level);
??enum Level
????{
????????Mid,
????????Low,
????????High
}
結(jié)果為Mid,如果把Mid的位置和Low互換則結(jié)果為Low,這是因為當(dāng)default操作符遇到枚舉類型會把它當(dāng)做數(shù)值型來處理,即第一個元素為0,后面的依次+1;
如果這樣寫:
enum Level
????{
????????Mid=1,
????????Low=0,
????????High=2
}則返回值為Low。當(dāng)用default獲取枚舉值的時候要小心,如果這樣寫:
enum Level
????{
????????Mid=1,
????????Low=3,
????????High=2
}返回值為0,出錯了,所以在設(shè)置枚舉值時最好給元素一個0的整數(shù)值。
?
先說明:關(guān)鍵字var:幫助生成隱式類型變量:
?int x;//顯式變量,明確的告訴了編譯器x屬于什么數(shù)據(jù)類型;
?????????var y;//隱式變量,告訴編譯器y的類型暫時不知道,當(dāng)我賦值的時候看著辦
C#是強(qiáng)類型語言變量一旦確定數(shù)據(jù)類型就不可以變更。
⑥new操作符:
*幫助我們在內(nèi)存當(dāng)中創(chuàng)建一個類型的實例并且立刻調(diào)用這個實例的實例構(gòu)造器(所謂的構(gòu)造函數(shù)),并取得的實例地址....
??new Form();//調(diào)用默認(rèn)實例構(gòu)造器
創(chuàng)建這個實例之后如果沒有任何變量去引用它,訪問它,過一會垃圾收集就把這個實例所占用的堆內(nèi)存當(dāng)做垃圾給收回來了。
*除了創(chuàng)建實例和調(diào)用實例構(gòu)造器之外還能把new取得的實例地址通過賦值符號交給負(fù)責(zé)訪問這個實例的變量。這樣就在變量和實例之間構(gòu)成了引用關(guān)系。有了這個引用關(guān)系之后就可以通過這個變量來訪問實例。如:?Form myForm=new Form();//調(diào)用默認(rèn)實例構(gòu)造器
??myForm.Text = "Hello!";//通過變量來訪問實例
?
*上面為主要功能,以下為附加功能:調(diào)用實例的初始化器:
??Form myForm = new Form() {Text="Hello!" };在實例后面加花括號里面加屬性的值。
可以初始化多個屬性,中間逗號隔開。
還有:有的時候用實例只是一次性的沒必要創(chuàng)建一個引用變量去初始化它,可以采用這時初始化器就發(fā)揮作用了:new Form(){Text=”Hello!”}.ShowDialog();只是由于沒有引用變量引用(沒有小孩牽著這個氣球,氣球一會就飛走了)所以一段時間后,垃圾回收器把它的堆內(nèi)存回收。
a、錯覺:要創(chuàng)建類的實例就一定要使用new操作符,錯誤的。如string Name = "Hello!";
String是一個類,創(chuàng)建實例時不用new操作符,這種方式叫做C#的”語法糖衣”,原因為為了統(tǒng)一使string與int的書寫格式,而把string類的new操作符隱藏起來了,string可以用new但平常不這么用。類似的還有數(shù)組:
用new操作符:
int[] myArray = new int[10];//由于int的實例構(gòu)造器有點特殊不用圓括號調(diào)用;
不用new操作符時:
?int[] myArray = { 1,2,3,4};
b、new操作符特殊用法:為匿名類型創(chuàng)建實例,
Form myForm=new Form(){Text=”Hello!”};當(dāng)為非匿名類型創(chuàng)建實例時new后面要加類型名,
當(dāng)為匿名類型創(chuàng)建實例時:如:
Var person=new {Name=”Mr li”,Age=34};//new操作符后面不跟類型,直接用初始化器初始化實例,什么類型?讓編譯器根據(jù)初始化內(nèi)容自行判斷,不過該實例一定要有引用變量引用,不知道類型?用var隱式變量即可。那到底是什么類型呢?
Console.WriteLine(Person.GetType().Name);
輸出為:<>f__AnonymousType0`2
“<>f__AnonymousType”為約定的前綴,0表示我在程序中創(chuàng)建的第一個,’2表示這個類型為泛型類,構(gòu)成這個類型的時候你需要兩個類型來構(gòu)成它,哪兩個類型呢?就是初始化器里面的一個是string,一個是int。這是在創(chuàng)建匿名類型時編譯器自己識別的類型。
這里才真正體現(xiàn)出var類型(全部)功能的強(qiáng)大之處與重要性。因為如上一種情況就算你想寫出它的類型也不知道叫什么名字。
*記住new操作符與var隱式變量組合的使用方法:是為匿名對象創(chuàng)建對象并且用隱式類型變量來引用這個實例。
c、new操作符有危險性(功能強(qiáng)大伴隨的濫用風(fēng)險)一旦在某個類里面(比如main函數(shù)隸屬的Program類)用new操作符創(chuàng)建了某個類的實例(比如在main函數(shù)中創(chuàng)建Form類),那么這個類(Form)就與主類(Program)緊緊耦合在一起,Pragram類就緊緊依賴于Form類,一旦某個類(Form)出現(xiàn)問題,整個耦合體都無法正常運行。即new操作符會造成緊耦合。那怎么解決?在軟件工程有項非常重要和實用的技術(shù)叫做”設(shè)計模式”,在”設(shè)計模式”當(dāng)中有一種非常重要的模式叫做”依賴注入”(dependenty injection),該模式就是幫助我們把緊耦合變成相對松的耦合。有概念即可:new操作符有風(fēng)險慎用,大型程序中為了避免有緊耦合的情況我們有一種叫做”依賴注入”的設(shè)計模式可以使用,實現(xiàn)不必關(guān)注。
*程序設(shè)計追求”高內(nèi)聚低耦合”
d、new關(guān)鍵字的多用性(不是操作符而是關(guān)鍵字):如
class Student
????{
????????public void Report()
????????{
????????????Console.WriteLine("I'm a student");
????????}
????}
????class CsStudent:Student
????{
????????new?public void Report()//這叫子類對父類方法的隱藏,這里的new便不是操作符而是修飾符用來修飾new后面的方法的。(并不常見)
????????{
????????????Console.WriteLine("I'm a Cstudent");
????????}
}
則?Student stu = new Student();
????????????stu.Report();
????????????CsStudent csStu = new CsStudent();
????????????csStu.Report();時分別調(diào)用各自的Report()方法。
?
⑦checked()和unchecked()操作符:用來檢查()內(nèi)的值在內(nèi)存中是否有溢出:(Overflow)
C#是強(qiáng)類型語言,任何一個變量它在內(nèi)存里面都有數(shù)據(jù)類型,而數(shù)據(jù)類型有個非常重要的作用就是表示這種數(shù)據(jù)類型的實例在內(nèi)存當(dāng)中能夠占多大的空間,一個值在內(nèi)存空間所占的大小決定了這個值能夠表達(dá)的范圍,一旦超出這個范圍這個值就產(chǎn)生了溢出。Checked就是告訴我們要去檢出溢出,unchecked則告訴我們不用:
?uint x = uint.MaxValue;
????????????Console.WriteLine(x);
????????????string binStr = Convert.ToString(x, 2);
????????????Console.WriteLine(binStr);
????????????try
????????????{
????????????????uint y = checked(x + 1);//檢測x+1是否溢出,溢出后去catch捕獲異常
????????????????Console.WriteLine(y);
????????????}
????????????catch (OverflowException ex)
????????????{
????????????????Console.WriteLine("There is overflow");
????????????}
Unchecked()操作符表示不用檢查,C#中默認(rèn)該種方式。Checked也有其他用法:
Checked
{
??try
????????????{
????????????????uint y = checked(x + 1);//檢測x+1是否溢出,溢出后去catch捕獲異常
????????????????Console.WriteLine(y);
????????????}
????????????catch (OverflowException ex)
????????????{
????????????????Console.WriteLine("There is overflow");
????????????}
}
直接判斷整個語句塊中所有語句是否有溢出。
?
⑧delegate操作符(關(guān)鍵字)最主要的作用為聲明一種叫委托的數(shù)據(jù)類型,委托是C#非常重要的概念。本節(jié)主要講其作為操作符的作用(非常稀有因為拉姆達(dá)表達(dá)式(Lambda Expressions)的出現(xiàn)就是來替代delegate當(dāng)做操作符的場景):使用delegate生成匿名方法:
?this.myButton.Click +=delegate (object sender, RoutedEventArgs e)//使用delegate聲明了一個匿名方法
????????{
????????????this.myTextBox.Text = "Hello World!";
????????};
程序原本應(yīng)為:this.myButton.Click += myButton_Click;
?void myButton_Click(object sender, RoutedEventArgs e)
????????{
????????????this.myTextBox.Text = "Hello World!";
????????}
現(xiàn)在替代這用用法的拉姆達(dá)表達(dá)式:
?this.myButton.Click += (sender, ?e)=>
????????{
????????????this.myTextBox.Text = "Hello World!";
????????};
語法的演變可見C#語法越來越簡潔,功能越來越強(qiáng)大。
?
⑨sizeof()操作符:
a、只能獲取結(jié)構(gòu)體類型在內(nèi)存中所占字節(jié)數(shù),默認(rèn)情況下:sizeof只能去獲取基本數(shù)據(jù)類型他們的實例在內(nèi)存當(dāng)中所占的字節(jié)數(shù),基本數(shù)據(jù)類型:比如int、uint...說白了就是C#關(guān)鍵字里面那些除了string和object的數(shù)據(jù)類型:因為這兩個為引用類。
b、在非默認(rèn)的情況下可以使用sizeof去獲取自定義的結(jié)構(gòu)體類型的實例它在內(nèi)存中占的字節(jié)數(shù),但是需要把它放在不安全的上下文當(dāng)中:
??unsafe
???{
????int x=sizeof(Student);
???}
Decimal數(shù)據(jù)類型精確度比double高占16個字節(jié);
⑩最后一個”基本操作符”:”->”
*類(class)屬于引用類型,結(jié)構(gòu)體(struct)屬于值類型。C#中有嚴(yán)格的規(guī)定像指針操作,取地址操作,用指針去訪問成員的操作,只能用來操作結(jié)構(gòu)體類型,不能用它們?nèi)ゲ僮饕皿w類型。(Class)
要運行不安全代碼除了要把它放在unsafe{}里面,還要再項目->最后一項(相應(yīng)項目屬性)->生成->勾選”允許生成不安全代碼”,所謂雙重保險。
使用該操作符時要在unsafe情況下使用:
unsafe
????????????{
????????????????Student stu;
????????????????stu.ID = 1;
????????????????stu.Score = 99;
????????????????Student*pStu=&stu;
????????????????pStu->Score = 100;
????????????????Console.WriteLine(stu.Score);
????????????}
?
4.2一元操作符:
只要有一個操作數(shù)跟在它后面就可以構(gòu)成表達(dá)式,也叫單目操作符。
?
①&x和*x操作符(很少見有印象即可):
這兩個操作符同樣也需要在不安全的上下文中運行:&是取地址操作符。
簡單錯誤:*pStu.錯誤:由于.為基本操作符優(yōu)先級大于*所以是先進(jìn)行pStu.的操作正確應(yīng)該為:(*pStu).即加個括號。
?
②+、-、!、~四個一元操作符:
-運算符可造成內(nèi)存溢出:
int a = int.MinValue;
?int b =checked( -a);//原因在于int.MaxValue與int.MinValue絕對值不一樣。
?
~求反操作符,對操作數(shù)轉(zhuǎn)化為2進(jìn)制,進(jìn)行二進(jìn)制的按位取反。
計算機(jī)取相反數(shù)的原理:先把該數(shù)轉(zhuǎn)化為2進(jìn)制再按位取反+1?
!操作符只能用來操作布爾類型的數(shù)據(jù),
?
③++x;--x運算符:單獨使用時x++與++x沒有區(qū)別。
?
4.3、強(qiáng)制類型轉(zhuǎn)化操作符:(T)x,T表示某種數(shù)據(jù)類型。
類型轉(zhuǎn)換:
①隱式(implicit)類型轉(zhuǎn)換,常見的為以下三種情況:
*不丟失精度的轉(zhuǎn)換:小字節(jié)數(shù)(存儲空間)的數(shù)據(jù)類型向多字節(jié)(存儲空間)的數(shù)據(jù)類型轉(zhuǎn)換不會丟失精度,比如金魚從魚缸里放到游泳池里還是好好的。例如:
??int x = int.MaxValue;
??long y = x;
這就是在不丟失精度的情況下進(jìn)行的隱式類型轉(zhuǎn)換,具體哪些數(shù)據(jù)類型可向哪些無丟失精度類型轉(zhuǎn)換見C#語言定義文檔6.1.2:
隱式數(shù)值轉(zhuǎn)換為:(原則為從小的向大的轉(zhuǎn))
l?從?sbyte?到?short、int、long、float、double?或?decimal。
l?從?byte?到?short、ushort、int、uint、long、ulong、float、double?或?decimal。
l?從?short?到?int、long、float、double?或?decimal。
l?從?ushort?到?int、uint、long、ulong、float、double?或?decimal。
l?從?int?到?long、float、double?或?decimal。
l?從?uint?到?long、ulong、float、double?或?decimal。
l?從?long?到?float、double?或?decimal。
l?從?ulong?到?float、double?或?decimal。
l?從?char?到?ushort、int、uint、long、ulong、float、double?或?decimal。
l?從?float?到?double。
從?int、uint、long?或?ulong?到?float?的轉(zhuǎn)換以及從?long?或?ulong?到?double?的轉(zhuǎn)換可能導(dǎo)致精度損失,但決不會影響數(shù)值大小。其他的隱式數(shù)值轉(zhuǎn)換決不會丟失任何信息。
不存在向?char?類型的隱式轉(zhuǎn)換,因此其他整型的值不會自動轉(zhuǎn)換為?char?類型。
?
*子類向父類的轉(zhuǎn)換;
例如定義三個互相繼承的類:
復(fù)制代碼
class Animal{public void Eat(){Console.WriteLine("Eat");}}class Human:Animal{public void Think(){Console.WriteLine("think");}}class Teacher:Human{public void Teach(){Console.WriteLine("Teach");}?
在main函數(shù)中:
?Teacher t = new Teacher();
????????????Human h = t;//這種就是子類向父類隱式類型換
而h.只能訪問Human類的成員,不能訪問Teacher類的成員,因為引用變量只能訪問它這個變量的類型它所具有的成員,注意是這個變量的類型是(Human)而不是這個變量引用的類型(Teacher)。(簡單的比喻比如人的父類是猴子,但猴子的只能做猴子的行為而不能做人類的行為。)
*裝箱;見上一節(jié)。
?
②顯式(explicit)類型轉(zhuǎn)換:為什么要有?因為這種轉(zhuǎn)換有精度的丟失,編譯器要你明明白白的寫出來,讓你知道有精度的丟失,也要轉(zhuǎn)換數(shù)據(jù)類型。
*有可能丟失精度(甚至發(fā)生錯誤)的轉(zhuǎn)換,即cast(鑄造):在數(shù)據(jù)前面加個圓括號:(T)x,T里面為強(qiáng)轉(zhuǎn)后的數(shù)據(jù)類型。
例如:
uint x = 65536;
ushort y = (ushort)x;//這樣才能成立,因為ushoort的最大值比x小
具體見語言定義文檔6.2.1;注意強(qiáng)制類型轉(zhuǎn)換時由有符號類型向無符號類型轉(zhuǎn)換,符號位拿來當(dāng)數(shù)據(jù)了肯定會丟失精度。所以在對有符號類型進(jìn)行強(qiáng)轉(zhuǎn)時要格外注意。
*拆箱
*使用Convert類:
首先:如下情況:
?string str1=Console.ReadLine();//ReadLine方法是讀取一行輸入
?string str2 = Console.ReadLine();
?Console.WriteLine( str1+str2);//
若輸入:12 12則輸出的是1212,原因為由于操作符與數(shù)據(jù)類型相關(guān)聯(lián),當(dāng)+識別到左右兩邊的數(shù)據(jù)都是string類型時則把兩個字符串連接起來,得到的就是1212的字符串而不是我們想要的值24。
正確做法為:?int x = Convert.ToInt32(str1);
????????????int y = Convert.ToInt32(str2);
這個例子就是非常常見的數(shù)據(jù)類型轉(zhuǎn)換例子。
?
有的數(shù)據(jù)類型不能使用cast這種方式如int和string這兩種數(shù)據(jù)類型差距太大。這時需要借助工具類(如Convert類)來進(jìn)行類型轉(zhuǎn)換。如:?Convert.ToInt32(x);這個類相當(dāng)于一個類型轉(zhuǎn)換樞紐,幾乎可以把當(dāng)前數(shù)據(jù)類型轉(zhuǎn)換成全部想要的數(shù)據(jù)類型。
*ToString方法與各類數(shù)據(jù)類型的Parse/TryParse方法:
有時候我們需要把其他數(shù)據(jù)如int轉(zhuǎn)化為字符串類型數(shù)據(jù)這時候有兩種方法可以選擇:
第一種:調(diào)用Conver類的ToString靜態(tài)方法:?Convert.ToString(36種重載形式);詳見”強(qiáng)制類型轉(zhuǎn)換ToString”
第二種:調(diào)用數(shù)值類型數(shù)據(jù)的ToString實例(非靜態(tài))方法來完成轉(zhuǎn)換:?x.ToString(3個重載);ToString方法但就是把實例轉(zhuǎn)化為一個字符串,創(chuàng)建一個Object對象:object o;o.會出現(xiàn)四個方法其中就有”ToString”說明所有的數(shù)據(jù)類型都有這個方法(一切數(shù)據(jù)類型都由object派生)
Parse(解析):這里的th1.Text和th2.Text都是字符串類型(string)
??double x = double.Parse(th1.Text);
????????????double y = double.Parse(th2.Text);
可以這樣改寫,小小的缺點:只能解析格式正確的數(shù)據(jù)如()里輸入1ab格式不對就不能自動解析并轉(zhuǎn)換為double類型。
這時可以使用TryParse方法:?double x = double.TryParse();
具體涉及內(nèi)容以后會講。
?
③自定義數(shù)據(jù)類型轉(zhuǎn)換操作符
示例;*操作符的本質(zhì)就是方法的簡記法
例如:創(chuàng)建了兩個類:
class Stone
????{
????????public int Age;
????}
????class Monkey
????{
????????public int Age;
}
在main函數(shù)中進(jìn)行類型轉(zhuǎn)換:
Stone stone = new Stone();
Monkey m = (Monkey)stone;
顯然不行編譯器不知道給怎么轉(zhuǎn)換,我們要告訴它,由于是stone進(jìn)行類型轉(zhuǎn)換所以在stone類里面添加具體語句:
class Stone
????{
????????public int Age;
????????public static explicit operator Monkey(Stone stone)//可以理解為顯式類型轉(zhuǎn)換操作符就是一個目標(biāo)類型的實例的構(gòu)造器,但是這個構(gòu)造器不是寫在目標(biāo)類型的類里面而是寫在被轉(zhuǎn)換的數(shù)據(jù)類型里面(而前面有四個操作符public static explicit operator ).
????????{
????????????Monkey m = new Monkey();
????????????m.Age = stone.Age / 500;//表示如何轉(zhuǎn)換
????????????return m;
????????}
????}
這樣編譯器就知道要怎么轉(zhuǎn)換了,繼續(xù)回到main函數(shù):
?Stone stone = new Stone();
?stone.Age = 5000;
?Monkey m = (Monkey)stone;
?Console.WriteLine(m.Age);
按照stone類里面定義(m.Age = stone.Age / 500;)的對stone對象進(jìn)行轉(zhuǎn)換,輸出為:10;
?
該方式為顯式轉(zhuǎn)換,隱式只需要:?public static implicit?operator Money(Stone stone)
即把?explicit改成implicit然后類型轉(zhuǎn)換時:?Money m = stone;可以把stone對象的前綴(Money)省略。
?
4.4、算術(shù)運算符:詳見語言定義文檔7.8.1
務(wù)必留意”類型提升”:算術(shù)運算符都會產(chǎn)生,默認(rèn)的類型提升如int型的數(shù)據(jù)與double的數(shù)據(jù)相乘為了不丟失精度,int型先隱式轉(zhuǎn)換為double再進(jìn)行計算。
“/“整數(shù)除法要注意除0異常,浮點數(shù)的除法沒有該異常。
?double a = double.PositiveInfinity;//正無窮大
?double b = double.NegativeInfinity;//負(fù)無窮大
?
double x=(double)5/4;
結(jié)果為浮點型的1.25因為()類型轉(zhuǎn)換操作符優(yōu)先級大于算術(shù)運算符,所以先對5進(jìn)行類型轉(zhuǎn)換為double再進(jìn)行除法,除法過程中運算符發(fā)現(xiàn)兩邊類型不同進(jìn)行”類型提升”
?
有時候取余操作符%也會進(jìn)行”類型提升”,也有浮點數(shù)取余。
?
4.5、位移操作符:<<(左移)、>>(右移)
表示:數(shù)據(jù)在內(nèi)存當(dāng)中的2進(jìn)制的結(jié)構(gòu)向左或者向右進(jìn)行一定位數(shù)的平移:
int x = 7;
int y = x << 1;//x左移一位
??string strx = Convert.ToString(x, 2).PadLeft(32, '0');
??string stry = Convert.ToString(y, 2).PadLeft(32, '0');
??Console.WriteLine(strx);
??Console.WriteLine(stry);
輸出顯示為:00000000000000000000000000000111
00000000000000000000000000001110明顯左移了一位
當(dāng)沒有溢出的情況下左移就是×2右移就是÷2.
移位過多會產(chǎn)生溢出由于默認(rèn)是unchecked所以不會有異常。
*左移無論是正負(fù)數(shù)補(bǔ)進(jìn)來的都是0,右移如果操作的是正數(shù)最高位補(bǔ)進(jìn)來的是0,負(fù)數(shù)則補(bǔ)1;
?
4.6、關(guān)系操作符<,>,<=,>=;
所有關(guān)系操作符的運算結(jié)果都是布爾類型的,除了可以比較數(shù)值類型還可以比較字符類型。
?char char1='a';
?char char2='A';
?ushort u1 = (ushort)char1;
?ushort u2 = (ushort)char2;結(jié)果為u1=97;u2=65,;(對應(yīng)Unicode碼表)
還可以比較字符串,只能比是否相等。
string str1 = "ABc";
string str2 = "Abc";
Console.WriteLine(str1.ToLower()==str2.ToLower());都轉(zhuǎn)換為小寫輸出結(jié)果為相等。
還可以調(diào)用:?string.Compare();返回正值則第一個大于第二個,負(fù)值則小于,0則相等,比較方式為把兩個字符串對齊依次比較字母對應(yīng)的Unicode碼。
?
4.7、類型檢驗操作符,is,as
is操作符結(jié)果是布爾類型,作用為檢驗?zāi)硞€對象是否為某個類(可以判斷分類):
Teacher t=new Teacher();
Var result=t is Teacher;
Var result=t is Animal;//由于Teacher的父類為Animal所以這個判斷也為true;反過來不行
as操作符的作用為:
Object o=new Teacher();
Teacher t=o as Teacher;即如果o對象是Teacher類則把o對象的地址交給Teacher類的引用變量t,否則把null值給t。
?
4.8、邏輯位與(&),邏輯位或(|),邏輯按位異或(^);
所謂位與就是按位求與,求與時把1當(dāng)做真,0當(dāng)做假,真&真為真,真&假為假,假&假為假。例如:
? int x = 7;
????????????int y = 28;
????????????int z = x & y;
????????????string strx = Convert.ToString(x, 2).PadLeft(32, '0');
????????????string stry = Convert.ToString(y, 2).PadLeft(32, '0');
????????????string strz = Convert.ToString(z, 2).PadLeft(32, '0');
????????????Console.WriteLine(strx);
????????????Console.WriteLine(stry);
????????????Console.WriteLine(strz);
輸出結(jié)果為:
00000000000000000000000000000111
00000000000000000000000000011100
00000000000000000000000000000100
按位或(|),真|真為真,假|(zhì)真為真,假|(zhì)假為假。
按位異或(^),兩位相同為真,不同為假。
?
4.9、條件與(&&),條件或(||):用來操作布爾類型的值,最后結(jié)果也是布爾類型的值。
條件與(&&):左右兩邊都是真,整體值才為真;條件或(||):左右兩邊有一個真整體即為真。
條件與(&)與條件或(|)具有短路效應(yīng):?if (x>y&&a++>3)當(dāng)條件較多時條件與只要發(fā)現(xiàn)左邊的值為假則不用判斷右邊的值了直接返回false,條件或情況類似,如果左邊為真則直接返回true。
?*由于存在該效應(yīng)我們寫代碼時要盡量避免。
?
4.10、null合并操作符(??):
?int ? y=null;//相當(dāng)于暫時給y賦null值以后可變
?int c = y ?? 1;//判斷y是否為null是就用1賦值給y。
?
4.11、條件操作符:?:(是唯一一個可以接收三個數(shù)的操作符):就是if..else分支的簡寫:
?y = (x >= 60)?? 100 : 0;表示x如果>=60就把100賦值給y否則把0賦值給y,往往用圓括號把條件括起來提高可讀性。
?
4.12、(優(yōu)先級最低)賦值表達(dá)式與lambda表達(dá)式:=,+=,%=,-=,>>=,<<=,&=,^=,|=
表示先運算再賦值,lambda表達(dá)式(=>)后面會講到。
不過都要注意數(shù)據(jù)類型,運算符會根據(jù)兩邊的數(shù)據(jù)類型來判斷進(jìn)行的是整數(shù)運算還是浮點數(shù)運算還是其他。*賦值運算符的運算順序是從右到左。例如:
Int x=3;
Int y=4;
Int z=5;
Int a=x+=y+=Z;先算右邊的y+=z即y=y+z;...
多抽出1分鐘來學(xué)習(xí),讓你的生命更加精彩!
總結(jié)
- 上一篇: 速度比肩移动SSD aigo U396
- 下一篇: Office Web Apps安装部署(