1. 程式人生 > >C#圖解教程 第八章 表示式和運算子

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