操作符概述

image.png

  • 操作符(Operator)也称为”运算符”
  • 操作符是用来操作数据的,被操作符操作的数据称为操作数(Operand)

操作符的本质

  • 操作符的本质是函数(即算法)的”简记法”
    • 假如没有发明”+”、只有Add函数,算式3+4+5将可以写成Add(Add(3,4),5)
    • 假如没有发明”x”、只有Mul函数,那么算式3+4*5将只能写成Add(3,Mul(4,5)),注意优先级
  • 操作符不能脱离与他相关联的数据类型
    • 可以说操作符就是与固定数据类型相关联的一套基本算法的简记法
    • 示例:为自定义数据类型创建操作符 ```csharp using System; using System.Collections.Generic;

namespace CreateOperator { class Program { static void Main(string[] args) { Person person1 = new Person(); Person person2 = new Person(); person1.Name = “Deer”; person2.Name = “Derr’wife”; //List nation = Person.GetMarry(person1,person2); List nation = person1 + person2; foreach (var n in nation) { Console.WriteLine(n.Name); } } }

  1. class Person
  2. {
  3. public string Name;
  4. //将operator +替代GetMarry
  5. public static List<Person> operator +(Person p1, Person p2)
  6. {
  7. List<Person> people = new List<Person>();
  8. people.Add(p1);
  9. people.Add(p2);
  10. for (int i = 0; i < 11; i++)
  11. {
  12. Person child = new Person();
  13. child.Name = p1.Name + "&" + p2.Name + "'s child" + (i+1);
  14. people.Add(child);
  15. }
  16. return people;
  17. }
  18. }

}

结果如下:<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/21507654/1623142170086-e7979458-5c62-4c21-b7d0-2545015033bd.png#clientId=uf895b960-f53c-4&from=paste&height=152&id=u66701854&margin=%5Bobject%20Object%5D&name=image.png&originHeight=303&originWidth=281&originalType=binary&ratio=2&size=18468&status=done&style=none&taskId=u1a925d20-31a2-4378-9f49-d710f50e499&width=140.5)

<a name="ejgXo"></a>
# 优先级与运算顺序

- 操作符的优先级
   - 可以使用圆括号提高被括起来表达式的优先级
   - 圆括号可以嵌套
   - 不像数学里有方括号和花括号,在C#语言里"[]"与"{}"有专门的用途
- 同优先级操作符的运算顺序
   - 除了带赋值功能的操作符,同优先级操作符由左向右运算
   - 带有赋值功能的操作符运算顺序是由右向左
   - 与数学运算不同,计算机语言的同优先级运算没有"结合率"
      - 3+4+5只能理解为Add(Add(3,4),5),不能理解为Add(3,Add(4,5))
   - 示例:
```csharp
int x;
x = 3 + 4 + 5;//不带赋值功能,同优先级操作符运算顺序:先算"3+4",再算"+5",最后进行赋值运算"="

int a = 100, b = 200, c = 300;
a += b += c;//带赋值功能,同优先级操作符运算顺序:由右向左
Console.WriteLine(a);//600
Console.WriteLine(b);//500
Console.WriteLine(c);//300

各类操作符的示例

基本操作符

成员访问操作符:x.y

  • 其作用有以下四点:
    • 可以访问外层名称空间中的子集名称空间
    • 可以访问名称空间中的类型
    • 可以访问类型的静态成员
    • 可以访问对象的成员
  • 示例: ```csharp using System.Windows.Forms;

namespace OperatorExample { class Program { static void Main(string[] args) { //访问外层名称空间中的子集名称空间:System.IO //访问名称空间中的类型:IO.File //访问类型的静态成员:File.Create System.IO.File.Create(“D:\HelloWord.txt”);

        //访问对象的成员:myForm.Text,myForm.ShowDialog()
        Form myForm = new Form();
        myForm.Text = "Hello,World";
        myForm.ShowDialog();
    }
}

}

<a name="PhBkv"></a>
### 
<a name="kCnQu"></a>
### 方法调用操作符:f(x)

   - 示例如下:
```csharp
using System;

namespace OperatorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            Calculator cal = new Calculator();
            double x = cal.Add(3, 4);//调用cal对象的Add实例方法
            Console.WriteLine(x);//访问Console类的WriteLine静态方法
            cal.PrintHello();//调用cal对象的PrintHello实例方法

            Action action = new Action(cal.PrintHello);//使用委托时,调用方法的“()”可省略
            action();
        }
    }

    class Calculator
    {
        public double Add(double a, double b)
        {
            return a + b;      
        }

        public void PrintHello()
        {
            Console.WriteLine("Hello");
        }
    }
}

元素访问操作符:a[x]

  • 作用是访问集合当中的元素
    • 访问数组元素 ```csharp //第一种方法创建数组元素,具有十个元素的数组,值都为初始值0 int[] myIntArray = new int[10];

//第二种方法创建数组元素,具有五个元素的数组(int[5]中的5可以省略),值分别为1,2,3,4,5,”{}”表示初始器 int[] myIntArray1 = new int[]{1,2, 3,4, 5};

//访问第一个元素 Console.WriteLine(myIntArray1[0]);//输出”1”


      - 访问字典元素
```csharp
using System;

namespace OperatorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            //将Name为索引,即类型为string;将Student当做值的类型
            Dictionary<string, Student> stuDic = new Dictionary<string, Student>();
            for (int i = 1; i <= ;i++)
            {
                Student stu = new Student();
                stu.Name = "s_" + i.ToString();
                stu.Score = 100 + i;
                stuDic.Add(stu.Name,stu);
            }

            //访问s_6的学生的成绩
            Student stu6 = stuDic["s_6"];
            Console.WriteLine(stu6.Score);//输出106
        }
    }

    class Student
    {
        public string Name;
        public int Score;
    }
}

后置自增、后置自减操作符:x++,x—

  • 示例:

    int x = 100;
    int y = x++;//先赋值,后自增。相当于int y = x; x++
    Console.WriteLine(x);//输出101
    Console.WriteLine(y);//输出100
    

    typeof操作符

  • 功能是查看一个类型的内部结构(Metadata)

  • 示例,查看int类型的名称空间,FullName,Name等信息

    static void Main(string[] args)
    {
    Type t = typeof(int);
    Console.WriteLine(t.Namespace);    
    Console.WriteLine(t.FullName); 
    Console.WriteLine(t.Name);
    int c = t.GetMethods().Length;
    foreach(var mi in t.GetMethods())
    {
          Console.WriteLine(mi.Name);
    }
    Console.WriteLine(c);
    }
    

    defult操作符

  • 功能是获取一个类型的默认值

  • 结构体类型、引用类型、枚举类型示例 ```csharp using System;

namespace OperatorExample { class Program { static void Main(string[] args) { int x = default(int); Console.WriteLine(x);//输出0,double也是0

        Form myForm = default(Form);
        Console.WriteLine(myForm == null);//输出True

        Level level = default(Level);
        Console.WriteLine(level);//输出为Low

        Level1 level1 = default(Level1);
        Console.WriteLine(level1);//输出为Mid
    }
}

enum Level
{
    Low,
    Mid,
    High
}

enum Level1
{
    Low = 1,
    Mid = 0,
    High = 2
}

}

<a name="KLLos"></a>
### 
<a name="RQvI7"></a>
### new操作符

   - 主要功能是在内存中创建一个类型的实例,并且立即调用该实例的实例构造器,还能得到实例的内存地址
   - 附加功能是还能调用实例的初始化器"{}"
   - var关键字可以申明隐式类型变量
   - new操作符功能强大,不能随便使用,每new一个对象,就会在类中造成强耦合。依赖注入可以把紧耦合变成松耦合
   - new也可以作为修饰符使用
   - 示例:
```csharp
using System;
using System.Windows.Forms;

namespace OperatorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            //通过new操作符,得到实例的内存地址,并通过赋值符号"="交给访问该实例的变量myForm,这样就在变量和实例之间构成了引用关系,有了此引用关系,就可以通过变量来访问实例
            Form myForm = new Form();
            myForm.Text = "Hello";
            myForm.ShowDialog();

            //调用实例的初始化器
            Form myForm1 = new Form(){Text = "Hello"};
            myForm1.ShowDialog();

            //也可以不需要引用变量来访问实例
            new Form(){Text = "Hello"}.ShowDialog();

            //有些类定义时,可不用new操作符,语法糖衣
            string name = "Bell"; //也可以加上new,string name = new string();
            int[] myArr = {1,2,3}; //也可以加上new,int[] myArr = new int[3];

            //匿名类型,new后面没有写具体的类型,也是体现var的真正强大功能
            var person = new {Name = "ok",Age = 30};
            Console.WriteLine(person.Name);//输出ok
            Console.WriteLine(person.Age);//输出30
            Console.WriteLine(person.GetType().Name);//输出<>f__AnonymousType 0'2

            Student stu = new Student();
            CsStudent csStu = new CsStudent();
            stu.Report();//输出I'm a student!
            csStu.Report();//输出I'm Cs student!
        }
    }

    class Student
    {
        public void Report()
        {
            Console.WriteLine("I'm a student!");
        }
    }

    class CsStudent : Student
    {
        //new修饰符,子类对父类方法进行隐藏,实际项目中不常用
        new public void Report()
        {
            Console.WriteLine("I'm Cs student!");
        }
    }
}

checked、unchecked操作符

  • 功能是检查值在内存中是否有溢出,checked会检查,unchecked相反不会检查
  • 示例:

    static void Main(string[] args)
    {
    uint x = uint.MaxValue;
    Console.WriteLine(x);//输出4294967295,即2^32-1
    string binStr = Convert.ToString(x,2);
    Console.WriteLine(binStr);//输出11111111 11111111 11111111 11111111
    
    uint y = x + 1;
    Console.WriteLine(y);//输出0,因为溢出了,超过了最大值范围
    
    //进行改写,操作符用法
    uint y = checked(x+1);//此时会报溢出异常错误
    
    //改成try catch方式
    try
    {
       uint y = checked(x+1);
       Console.WriteLine(y);
    }
    catch(OverflowException ex)
    {
       Console.WriteLine("There's overflow");
    }
    
    //上下文范围用法
    checked
    {
       try
       {
           uint y = x+1;
           Console.WriteLine(y);
       }
       catch(OverflowException ex)
       {
           Console.WriteLine("There's overflow");
       }
    }
    }
    

    delegate操作符

  • delegate关键字最常用于委托,现在比较少把它当成操作符使用,是一种过时的技术,因为现在常用lambda表达式替代

  • 示例:在WPF程序中有一个myButton按钮,点击按钮会触发一个事件,如下

    namesapce Example
    {
    public partial class MainWindow : Window
    {
       public MainWindow()
       {
           InitalizeComponent();
    
           //正常用法
           this.myButton.Click += myButton_Click;
    
           //使用delegate操作符申明匿名方法
           this.myButton.Click += delegate(object sender,RoutedEventArgs e)
           {
               this.myTextBox.Text = "Hello";
           }
    
           //使用lambda表达式
           this.myButton.Click += ( sender, e) =>
           {
               this.myTextBox.Text = "Hello";
           }
       }
    
       //正常非匿名的方式
       void myButton_Click(object sender,RoutedEventArgs e)
       {
           this.myTextBox.Text = "Hello";
       }
    }
    }
    

    sizeof操作符

  • 功能是获取一个对象在内存中所占字节数的尺寸,需注意:

    • 默认只能用于基本数据类型(结构体类型)的实例在内存中占字节数(除了String和Object)
    • 非默认下,可用于自定义的结构体类型实例在内存中占的字节数,但需在不安全上下文中 ```csharp using System;

namespace OperatorExample { class Program { static void Main(string[] args) { int x = sizeof(int);//x值为4 int y = sizeof(double);//y值为8

        unsafe
        {
            //需在build -> 允许不安全代码
            int z = sizeof(Student);//z值为16,并非12
        }     
    }
}

struct Student
{
    int ID;
    long Score;
}

}

<a name="eQZAW"></a>
### 
<a name="MYEzq"></a>
### 箭头操作符:->

   - 只能操作结构体类型,不能操作引用类型
   - 示例:
```csharp
using System;

namespace OperatorExample
{
    class Program
    {
        static void Main(string[] args)
        {
            unsafe
            {
                Student stu;
                stu.ID = 1;
                stu.Score = 99;
                Student* pStu = &stu;//"*"为引用操作符,"&"为取地址操作符,pStu为指针变量
                pStu->Score = 100;
                Console.WriteLine(stu.Score);//输出100
            } 
        }
    }

    struct Student
    {
        int ID;
        long Score;
    }
}

一元操作符

取地址操作符:&x,引用操作符:*x

  • 和箭头操作符:->一样,实际工作应用较少
  • 以上示例,对pStu进行成员访问时,也可以下面方法:
    (* pStu).Score = 1000;//由于成员访问"."操作符优先级比*x高,所以需要用"()"来提高运算顺序
    Console.WriteLine(stu.Score);//输出1000
    

正操作符:+,负操作符:- ,非操作符:!,取反操作符: ~

  • C#求相反数是按位取反在加1
  • 非操作符!,结果只有true或者false
  • 示例:

    static void Main(string[] args)
    {
    int x = int.MinValue;//int 值范围-2^32到2^32-1
    int y = -x;
    Console.WriteLine(x);//输出-2^32
    Console.WriteLine(y);//输出-2^32
    
    //如果改写成如下
    int z = checked(-x);//报错溢出
    
    int a = 12345678;
    int b = ~a;//按位取反
    int c = b+1;
    string aStr = Convert.ToString(a,2).PadLeft(32,'0'); 
    //值为00000000 10111100 01100001 01001110
    
    string bStr = Convert.ToString(b,2).PadLeft(32,'0');
    //值为11111111 01000011 10011110 10110001
    
    string cStr = Convert.ToString(c,2).PadLeft(32,'0');
    //值为11111111 01000011 10011110 10110010,即为-12345678
    }
    

前置自增、前置自减操作符:++x,—x

  • 示例:
    int x = 100;
    int y = ++x;//先自增,后赋值。相当于 x = x + 1;int y = x
    Console.WriteLine(x);//输出101
    Console.WriteLine(y);//输出101
    

强制类型转换操作符:(T)x

  • 隐式(implicit)类型转换
    • 不丢失精度的转换

image.png

  • 子类向父类的转换
  • 装箱
  • 示例:不丢失精度,子类向父类的转换 ```csharp using System;

namespace OperatorExample { class Program { static void Main(string[] args) { int x = int.MaxValue; long y = x;//隐式转换,不丢失精度

        //由子类向父类的隐式转换,即多态
        Teacher t = new Teacher();
        Human h = t;//h没有t的Teach方法,只有Think和Eat方法
        Animal a = h;//a没有Teach和Think方法,只有Eat方法

    }
}

class Animal
{
    public void Eat()
    {
        Console.WriteLine("Eating...");
    }
}

class Human : Animal
{
    public void Think() 
    {
        Console.WriteLine("Who I am?");
    }
}

class Teacher : Human
{
    public void Teach()
    {
        Console.WriteLine("I teach programming.");
    }
}

}


- 显式(explicit)类型转换

![image.png](https://cdn.nlark.com/yuque/0/2021/png/21507654/1623207099694-b361e2b6-3e98-4fb4-9b88-6e0c1d06b4ad.png#clientId=u4698cf6d-8c36-4&from=paste&height=298&id=hrl1x&margin=%5Bobject%20Object%5D&name=image.png&originHeight=596&originWidth=921&originalType=binary&ratio=2&size=214473&status=done&style=none&taskId=u0adfe4c0-a983-4334-8de6-24692c76270&width=460.5)

   - 有可能丢失精度(甚至发生错误)的转换,即cast,(T)x
   - 拆箱
   - 使用Convert类,用于两种类型差距太大,不能用cast进行转换,如string与int之间转换
   - ToString方法与各数据类型的Parse/TryParse
   - 示例:cast转换,Convert类转换
```csharp
static void Main(string[] args)
{
    Console.WriteLine(ushort.MaxValue);//输出65535,二进制为11111111 11111111
    uint x = 65536;//二进制为1 00000000 00000000
    ushort y = (ushort)x;//cast方式进行转换
    Console.WriteLine(y);//输出0,因为ushort最大为65535,x超过了最大值,所以只能装下16位,即后面的00000000 00000000,第17位的1会被丢失

    //Convert方式进行转换
    Console.WriteLine("请输入第1个数值");
    int a = Convert.ToInt32(Console.ReadLine());
    Console.WriteLine("请输入第2个数值");
    int b = Convert.ToInt32(Console.ReadLine());
    int c = a + b;
    string cStr = Convert.ToString(c, 2);
    Console.WriteLine(cStr);
    Console.WriteLine($"{a}+{b}={c}");

    //Parse方式进行转换,只能对值类型进行转换,否则会报错,tryParse则多了一个bool类型的输出参数
    Console.WriteLine("请输入第3个数值");
    double p1 = double.Parse(Console.ReadLine());
    Console.WriteLine(p1);

    //TryParse方式进行转换,多了一个bool类型的输出参数,如果输出非数字,则结果为false
    Console.WriteLine("请输入第4个数值");
    bool p2Result = false;
    double p2;
    p2Result = double.TryParse(Console.ReadLine(), out p2);
    Console.WriteLine(p2);
    Console.WriteLine(p2Result);
}
  • 自定义类型转换操作符
    • 示例:自定义显示转换和隐式转换 ```csharp using System; using System.Collections.Generic; using System.Windows.Forms;

namespace OperatorExample { class Program { static void Main(string[] args) { Stone stone = new Stone(); stone.Age = 5000; //Monkey wukongSun = (Monkey)stone;//显式转换 Monkey wukongSun = stone;//隐式转换 Console.WriteLine(wukongSun.Age);//输出10 } }

class Stone
{
    public int Age;

    //显式转换
    //public static explicit operator Monkey(Stone stone)
    //{
    //    Monkey m = new Monkey();
    //    m.Age = stone.Age / 500;
    //    return m;
    //}

    //隐式转换
    public static implicit operator Monkey(Stone stone)
    {
        Monkey m = new Monkey();
        m.Age = stone.Age / 500;
        return m;
    }
}

class Monkey
{
    public int Age;
}

}


<a name="IvqIe"></a>
## 乘法、加法操作符

- 乘法操作符:"*"、"/"、"%"
- 加法操作符:"+"、"-"
- 具体定义可以参考《C#语言规范》文档的第7.8章节-算术运算符
- 它们统称为算数运算操作符,与数学的基本一致,但也有以下不同点
   - C#中的这些操作符与其数据类型相关
   - C#中有取余"%"操作符,而数学中没有
   - C#中进行算数运算时,务必留意"数值提升"

<a name="H7kIg"></a>
## 位移操作符

- 位移操作符:<<,>>
- 表示数据在内存中的二进制的结构向左或向右进行一定位置的平移
- 在不参数溢出的情况下,左移就是让数据乘2,右移就是让数据除2
- 左移,无论是正数还是负数,补进来的都是0;
- 右移,如果是正数,最高位补进来的是0,如果是负数,最高位补进来的是1
```csharp
static void Main(string[] args)
{
    int x = 7;
    int y = x << 2;
    string strX = Convert.ToString(x, 2).PadLeft(32, '0');
    string strY = Convert.ToString(y, 2).PadLeft(32, '0');
    Console.WriteLine(strX);//00000000000000000000000000000111
    Console.WriteLine(strY);//00000000000000000000000000011100
    Console.WriteLine(y);//28,28=7*2^2
}

关系和类型检测、相等操作符

  • 关系和类型检测操作符:<,>,<=,>=,is,as
  • is操作符结果是布尔类型,as操作符结果要么返回null,要么返回对象的类型
  • 相等操作符:==,!=
  • 所有关系运算操作符运算的结果都是布尔类型 ```csharp using System;

namespace OperatorExample { class Program { static void Main(string[] args) { //关系操作符,比较数值类型 int x = 7; double y = 4.0; var result = x > y; //var result = x >= y; //输出为true //var result = x < y; //输出为false //var result = x <= y; //输出为false //var result = x == y; //输出为false //var result = x != y; //输出为true Console.WriteLine(result.GetType().FullName); //输出为System.Boolean Console.WriteLine(result); //输出为true

        //比较字符类型,char类型是整数类型,它是由2个字节保存一个无符号短整型的数,该数对应Unicode码表上的字符
        char c1 = 'a';
        char c2 = 'A';
        ushort u1 = (ushort)c1;
        ushort u2 = (ushort)c2;
        var carResult = c1 > c2;
        Console.WriteLine(u1); //输出为97
        Console.WriteLine(u2); //输出为65
        Console.WriteLine(carResult); //输出为true

        //比较字符串类型
        string str1 = "Abc";
        string str2 = "abc";
        Console.WriteLine(str1==str2);//输出为false
        Console.WriteLine(str1.ToLower() == str2.ToLower());//输出为true

        //is操作符
        Teacher teacher = new Teacher();
        var resTeacher = teacher is Human;
        Console.WriteLine(resTeacher.GetType().FullName); //输出为System.Boolean
        Console.WriteLine(resTeacher); //输出为true

        //as操作符,要么返回null,要么返回对象的类型
        object o = new Teacher();
        Teacher t = o as Teacher;
        if (t!=null)
        {
            t.Teach();//输出I teach programming.
        }
    }
}

class Animal
{
    public void Eat()
    {
        Console.WriteLine("Eating...");
    }
}

class Human : Animal
{
    public void Think()
    {
        Console.WriteLine("Who I am?");
    }
}

class Teacher : Human
{
    public void Teach()
    {
        Console.WriteLine("I teach programming.");
    }
}

}


<a name="Wei1Q"></a>
## 逻辑"与",逻辑"异或",逻辑"或"操作符

- 逻辑"与"(AND)操作符:&
- 逻辑"异或"(XOR)操作符:^
- 逻辑"或"(OR)操作符:|
- 都是用来操作数据的二进制结构,因此也称"位与","位异或","位或"
```csharp
static void Main(string[] args)
{
    int a = 7;
    int b = 28;
    int x = a & b;
    int y = a | b;
    int z = a ^ b;
    string strA = Convert.ToString(a, 2).PadLeft(32, '0');
    string strB = Convert.ToString(b, 2).PadLeft(32, '0');
    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(strA);//00000000000000000000000000000111
    Console.WriteLine(strB);//00000000000000000000000000011100

    Console.WriteLine(strX);//00000000000000000000000000000100
    Console.WriteLine(strY);//00000000000000000000000000011111
    Console.WriteLine(strZ);//00000000000000000000000000011011
}

条件”与”,条件”或”操作符

  • 条件”与”(AND)操作符:&&
  • 条件”或”(OR)操作符:||
  • 比较的数为布尔类型,结果也为布尔类型

    static void Main(string[] args)
    {
      int a = 5;
      int b = 4;
      int x = 100;
      int y = 200;
      if (a > b && x < y)
      {
          Console.WriteLine("Hello");//输出Hello
      }
    
      if (a > b || x < y)
      {
          Console.WriteLine("Hello");//输出Hello
      }
    
      //a++ > 5,先比较,后自增,因此为false,代码块内不执行
      if (x < y && a++ > 5)
      {
          Console.WriteLine("Hello");//不输出
      }
      Console.WriteLine(a);//输出为6
    }
    

null合并操作符

  • null合并操作符:??

    static void Main(string[] args)
    {
      //可空类型Nullable<>
      Nullable<int> x = null;
      x = 100;
      Console.WriteLine(x);//输出100
    
      //吸收为关键字
      int? y = null;
      y = 99;
      Console.WriteLine(y);//输出99
    
      int z = y ?? 1;//y是否null,如果是就给z赋值1
      Console.WriteLine(z);//输出99
    }
    

条件操作符

  • 条件操作符:?:
  • 本质就是if…else…分支的一个简写

    static void Main(string[] args)
    {
      int x = 80;
      string str = string.Empty;
      if (x >= 60)
      {
          str = "Pass";
      }
      else
      {
          str = "Failed";
      }
      Console.WriteLine(str);
    
      string str1 = (x >= 60) ? "Pass" : "Failed";
      Console.WriteLine(str1);
    }
    

赋值和lambda表达式操作符

  • 赋值操作符:=,*=,/=,%=,+=,-=,<<=,>>=,&=,^=,|=
  • 运算顺序:从右向左
  • lambda表达式操作符:=>

    static void Main(string[] args)
    {
      int x = 5;
      x += 1;//相当于:x = x + 1
      Console.WriteLine(x);//输出为6
    
      x <<= 1;//相当于:x = x << 1
      Console.WriteLine(x);//输出为12=6*2^1
    
      int a = 5,b =6, c = 7;
      int z = a += b *= c;//先算 b *= c 值为42,再算 a += 42 值为47,最后在赋值z = 47
      Console.WriteLine(z)//输出为47
    }