zoukankan      html  css  js  c++  java
  • 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='
    ';
    char c3='x0061';
    char c4='u005a';

    一些特殊字符及其编码见下图


    字符串字面量

    两种字符串字面量类型:

    • 常规字符串字面量
    • 逐字字符串字面量

    常规字符串字面量包含:

    • 字符
    • 简单转义序列
    • 十六进制和Unicode转义序列
    string st1="Hi there!";
    string st2="Val	5,val	10";
    string st3="Addx000ASomeu0007Interest";

    逐字字符串以@为前缀,它有以下特征:

    • 逐字字符串与常规字符串区别在于转义字符串不会被求值。在双引号中间的所有内容,包括通常被认为是转义序列的内容,都被严格按字符串中列出的那样打印
    • 逐字字符串的唯一例外是相邻的双引号组,它们被解释为单个双引号字符
    string rst1="Hi there!";
    string vst1=@"Hi there!";
    string rst2="It started,"Four score and seven..."";
    string vst2=@"It started,""Four score and seven...""";
    string rst3="Value 1 	 5,val2 	 10";
    string vst3=@"Value 1 	 5,val2 	 10";
    string rst4="C:\Program Files\Microsoft\";
    string vst4=@"C:Program FilesMicrosoft";
    string rst5=" Print x000A Multiple u000A Lines";
    string vst5=@" 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章)。

  • 相关阅读:
    iOS崩溃报告获取一
    GCDTimer
    Runtime
    Socket
    冒泡排序笔记
    学习java虚拟机笔记
    ftp发送文件包括中文名
    java email
    批量数据插入高效 转发
    读取本地硬盘文件,快速扫描插入 数据库
  • 原文地址:https://www.cnblogs.com/moonache/p/6140985.html
Copyright © 2011-2022 走看看