C#圖解教程 第八章 表示式和運算子
表示式和運算子
表示式
本章將定義表示式,並描述C#提供的運算子。
運算子是一個符號,它表示返回單個結果的操作。運算元(operand)指作為運算子輸入的資料元素。一個運算子會:
- 將運算元作為輸入
- 執行某個操作
- 基於該操作返回一個值
表示式是運算子和運算元的字串。可以作為運算元的結構有:
- 字面量
- 常量
- 變數
- 方法呼叫
- 元素訪問器,如陣列訪問器和索引器
- 其他表示式
例:下面的表示式,有3個運算子和4個運算元
字面量
字面量(literal)是原始碼中鍵入的數字或字串,表示一個指定型別的明確的、固定的值。
例:字面量
class Program { static void Main() { Console.WriteLine("{0}",1024);//整數字面量 Console.WriteLine("{0}",3.1416F);//浮點型字面量 Console.WriteLine("{0}",true);//布林型字面量 } }
對於引用型別變數,字面量null表示變數沒有設定為記憶體中的資料。
整數字面量
例:不同的整數型別
236 //整型 236L //長整型 236U //無符號整型 236UL//無符號長整型
整數字面量還可以寫成十六進位制(hex)形式
實數字面量
實數字面量組成如下:
- 十進位制數字
- 可選的小數點
- 可選的指數部分
- 可選的字尾
例:實數字面量的不同格式
float f1=236F; double d1=236.712; double d2=.351; double d3=6.338e-26;
無後綴的實數字面量預設是double型別。
字元字面量
字元字面量可以是下面任意一種:
- 單個字元
- 簡單轉義序列:反斜槓+單個字元
- 十六進位制轉義序列:反斜槓+大寫或小寫x+4個十六進位制數
- Unicode轉義序列:反斜槓+大寫或小寫u+4個十六進位制數
例:字元字面量的不同格式
char c1='d'; char c2='\n'; char c3='\x0061'; char c4='\u005a';
一些特殊字元及其編碼見下圖
字串字面量
兩種字串字面量型別:
- 常規字串字面量
- 逐字字串字面量
常規字串字面量包含:
- 字元
- 簡單轉義序列
- 十六進位制和Unicode轉義序列
string st1="Hi there!"; string st2="Val\t5,val\t10"; string st3="Add\x000ASome\u0007Interest";
逐字字串以@為字首,它有以下特徵:
- 逐字字串與常規字串區別在於轉義字串不會被求值。在雙引號中間的所有內容,包括通常被認為是轉義序列的內容,都被嚴格按字串中列出的那樣列印
- 逐字字串的唯一例外是相鄰的雙引號組,它們被解釋為單個雙引號字元
string rst1="Hi there!"; string [email protected]"Hi there!"; string rst2="It started,\"Four score and seven...\""; string [email protected]"It started,""Four score and seven..."""; string rst3="Value 1 \t 5,val2 \t 10"; string [email protected]"Value 1 \t 5,val2 \t 10"; string rst4="C:\\Program Files\\Microsoft\\"; string [email protected]"C:\Program Files\Microsoft\"; string rst5=" Print \x000A Multiple \u000A Lines"; string [email protected]" Print Multiple Lines";
編譯器讓相同的字串字面量共享堆中同一記憶體位置以節約記憶體
求值順序
表示式可以由許多巢狀的子表示式構成。子表示式的求值順序可以使表示式最終值發生差別。
優先順序
正如小學的先乘除再加減,C#中運算子也有優先順序。
結合性
表示式中運算子優先順序不同的,從高到低依次運算。但若是運算子優先順序相同怎麼辦?
當連續運算子有相同優先順序時,求值順序由操作結合性決定。
- 左結合運算子從左至右
- 右結合運算子從右至左
- 除賦值運算子外,其他二元運算子都是左結合
- 賦值運算子和條件運算子是右結合
簡單算術運算子
求餘運算子
求餘運算子(%)用第二個運算元除第一個運算元,並返回餘數。
求餘運算子是二元左結合運算子。
- 0%3=0,因為0除3得0餘0
- 2%3=2,因為2除3的0餘2
- 4%3=1,因為4除3得1餘1
關係比較運算子和相等比較運算子
它們都是二元左結合運算子,對其運算元進行比較並返回bool值。
與C和C++不同,在C#中數字不具有布林意義
int x=5; if(x)//錯,x是int型別,不是布林型 if(x==5)//對,返回true
比較操作和相等性操作
對於大多數引用型別來說,比較它們的相等性,將只比較它們的引用。
- 如果引用相等,即它們指向記憶體中相同物件,相等性為true,否則為false,即使記憶體中兩個分離的物件在所有其他方面完全相等。
- 這稱為淺比較
下圖闡明瞭引用型別的比較
- 圖左邊,a和b兩者引用相同,返回true
- 圖右邊,引用不同,所以即使內容相同,也返回false
string型別也是引用型別,但它的比較方式不同。比較字串的相等性,將比較它們的長度和內容(區分大小寫)
- 如果兩個字串長度和內容相等則返回true,即使它們佔用不同記憶體區域
- 這稱為深比較(deep comparison)
將在第15章介紹的委託也是引用型別,並且也使用深比較。比較委託的相等性時,讓兩個委託都是null,或兩者的呼叫列表有相同數目成員,並且呼叫列表想匹配,則返回true。
比較數值表示式,將比較型別和值。比較enum型別時,比較運算元的實際值。列舉在第13章闡述。
遞增運算子和遞減運算子
無論運算子前置還是後置,隻影響返回給表示式的值。在語句執行後,最終存放在運算元的變數的值相同
條件邏輯運算子
條件邏輯運算子使用“短路”(short circuit)模式操作,意思是,如果計算Expr1之後結果已確定,那麼它會跳過Expr2的求值。
例:短路示例
bool bVal; bVal=(1==2)&&(2==2); //左側false,接著&&運算,結果必是false,所以跳過了右側的運算 bVal=(1==1)||(1==2); //左側true,接著是||運算,結果必是true,所以跳過了右側的運算
因為短路特性,不要在Exp2中放置帶有副作用的表示式(比如改變一個值),因為可能不會計算。
bool bVal;int iVal=10; bVal=(1==2)&&(9==iVal++);//結果:bVal=False,iVal=10; ↑ ↑ False 不會計算
邏輯運算子
移位運算子
例:移位運算子示例
- 運算元14的每個位向左移動3個位置
- 右邊結尾騰出位置用0補充
- 結果為112
int a,b,x=14; a=x<<3; b=x>>3; Console.WriteLine("{0}<<3={1}",x,a); Console.WriteLine("{0}>>3={1}",x,b);
賦值運算子
賦值運算子是二元右結合運算子
複合賦值
複合賦值運算子允許一種速記方法,在某些情況下避免左邊的變數在右邊重複出現。
複合賦值不僅更短,也易於理解。
x=x+(y-z); x+=y-z;
條件運算子
條件運算子是一種強大且簡潔的方法,基於條件的結果,返回兩個值之一。
條件運算子是三元運算子
- 格式:Condition?Expression1:Expression2
- Condition必須返回一個bool型別的值
- 如果Condition求值為true,那麼對Expression1求值並返回。否則,對Expression2求值並返回
if...else if(x<y) intVar=5; else intVar=10; 條件運算子 intVar=x<y?5:10;
if…else語句是控制流語句,它應當用來做兩個行為中的一個。條件運算子返回一個表示式,它應當用於返回兩個值中的一個。
使用者定義的型別轉換
使用者定義的轉換將在第16章詳講,在這裡稍微提一下。
- 可以為自己的類和結構定義隱式和顯式轉換。這允許把使用者定義型別的物件轉換成某個其他型別
- C#提供隱式轉換和顯示轉換
- 隱式轉換,當決定在特定上下文中使用特定型別時,如有必要,編譯器會自動執行轉換
- 顯式轉換,編譯器只在使用顯式轉換運算子時才執行轉換
宣告隱式轉換的語法如下。
必需的 目標型別 源資料 ↓ ↓ ↓ public static implicit operator TargetType(SourceType Identifier) { ... return ObjectOfTargetType; }
顯式轉換的語法與之相同,但要用explicit替換implicit
例:將LimitedInt轉換為int
class LimitedInt { const int MaxValue=100; const int MinValue=0; public static implicit operator int(LimitedInt li) { return li.TheValue; } public static implicit operator LimitedInt(int x) { var li=new LimitedInt(); li.TheValue=x; return li; } private int _theValue=0; public int TheValue { get{return _theValue;} set { if(value<MinValue) _theValue=0; else _theValue=value>MaxValue?MaxValue:value; } } class Program { static void Main() { LimitedInt li=500; int value=li; Console.WriteLine("li:{0},value:{1}",li.TheValue,value); } } }
顯式轉換和強制轉換運算子
如果把兩個運算子宣告為explicit,你將不得不在實行轉換時顯示使用轉換運算子。
public static explicit operator int(LimitedInt li) { return li.TheValue; } public static explicit operator LimitedInt(int x) { var li=new LimitedInt(); li.TheValue=x; return li; } static void Main() { LimitedInt li=(LimitedInt)500; int value=(int)li; Console.WriteLine("li:{0},value:{1}",li.TheValue,value); }
輸出結果與上例相同
另外有兩個運算子,接受一種型別的值,並返回另一種不同的、指定型別的值。這就是is運算子和as運算子。它們將在第16章結尾闡述。
運算子過載
運算子過載允許你定義C#運算子應該如何操作自定義型別的運算元
- 運算子過載只能用於類和結構
- 宣告必須同時使用static和public修飾符
- 運算子必須是要操作的類或結構的成員
例:類LimitedInt的兩個過載運算子,加運算子和減運算子
class LimitedInt { 必需的 型別 關鍵字 運算子 運算元 ↓ ↓ ↓ ↓ ↓ public static LimitedInt operator + (LimitedInt x,double y) { var li=new LimitedInt(); li.TheValue=x.TheValue+(int)y; return li; } public static LimitedInt operator - (LimitedInt x) { var li=new LimitedInt(); li.TheValue=0; return li; } ... }
運算子過載的限制
不是所有的運算子都能被過載,可以過載的型別也有限制。
遞增和遞減運算子可過載。但和預定義的版本不同,過載運算子的前置和後置之間沒有區別。
運算子過載不能做下面的事情:
- 建立新運算子
- 改變運算子的語法
- 重新定義運算子如何處理預定義型別
- 改變運算子的優先順序或結合性
過載運算子應該符合運算子的直觀含義。
運算子過載的示例
例:LimitedInt的3個運算子過載
class LimitedInt { const int MaxValue=100; const int MinValue=0; public static LimitedInt operator - (LimitedInt x) { var li=new LimitedInt(); li.TheValue=0; return li; } public static LimitedInt operator - (LimitedInt x,LimitedInt y) { var li=new LimitedInt(); li.TheValue=x.TheValue-y.TheValue>0?(x.TheValue-y.TheValue):0; return li; } public static LimitedInt operator + (LimitedInt x,double y) { var li=new LimitedInt(); li.TheValue=x.TheValue+(int)y; return li; } private int _theValue=0; public int TheValue { get{return _theValue;} set { if(value<MinValue) _theValue=0; else _theValue=value>MaxValue?MaxValue:value; } } class Program { static void Main() { var li1=new LimitedInt(); var li2=new LimitedInt(); var li3=new LimitedInt(); li1.TheValue=10;li2.TheValue=26; Console.WriteLine("li:{0},li2:{1}",li1.TheValue,li2.TheValue); li3=-li1; Console.WriteLine("-{0}={1}",li1.TheValue,li3.TheValue); li3=li2-li1; Console.WriteLine("{0}-{1}={2}",li2.TheValue,li1.TheValue,li3.TheValue); li3=li1-li2; Consoel.WriteLine("{0}-{1}={2}",li1.TheValue,li2.TheValue,li3.TheValue); } } }
typeof運算子
typeof運算子返回作為其引數的任何型別的System.Type物件。
例:使用typeof運算子獲取SomeClass類的資訊
using System.Reflection;//反射 class SomeClass { public int Field1; public int Field2; public void Method1(){} public int Method2(){return 1;} } class Program { static void Main() { var t=typeof(SomeClass); FieldInfo[] fi=t.GetFields(); MethodInfo[] mi=t.GetMethods(); foreach(var f in fi) Console.WriteLine("Field:{0}",f.Name); foreach(var m in mi) Console.WriteLine("Method:{0}",m.Name); } }
GetType方法也會呼叫typeof運算子,該方法對每個型別的每個物件都有效。
例:使用GetType獲取物件型別名稱
class SomeClass { } class Program { static void Main() { var s=new SomeClass(); Console.WriteLine("Type s:{0}",s.GetType().Name); } }
其他運算子
本章介紹的運算子是內建型別的標準運算子。本書後面部分會介紹其他特殊用法的運算子及運算元型別。例如,可空型別有一個特殊運算子叫空接合運算子(第25章)。
from: http://www.cnblogs.com/moonache/p/6140985.html