操作符概览

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

    操作符的本质

  • 操作符的本质是函数(即算法)的“简记法”

  • 操作符不能脱离与它关联的数据类型

    • 操作符有与其返回值关联(有关)的数据类型

      运算符重载

  • operator + 重载“+”运算符

  • 可以将运算符进行重载,当在某个对象里面创建了运算符重载,就可以将此对象像数据一样进行操作。

    操作符的优先级

  • 可以使用圆括号提高被括起来表达式的优先级

  • 可以嵌套
  • 只能说圆括号,中括号和大括号有其他用途
  • C#同优先级运算规则
    • 常规从左到右进行运算
    • 赋值功能从右往左运算
    • 没有“结合律”

10 11 12 运算符详解 - 图1

f(x) 方法调用操作符

C# 里方法调用都要用到()
Action 是委托,委托在创建时只需要知道方法的名称,不调用方法,所以只会用到方法名(不加())。当然最终myAction();也用到了方法调用操作符()

  1. namespace OperatorsExample
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. var c = new Calculator();
  8. double x = c.Add(3.0, 4.6);
  9. Console.WriteLine(x);
  10. Action myAction = new Action(c.PrintHello); //声明一个委托,然后将方法加进委托
  11. myAction(); //当我们调用委托时就会自动调用对象的方法
  12. }
  13. }
  14. class Calculator
  15. {
  16. public double Add(double a,double b)
  17. {
  18. return a + b;
  19. }
  20. public void PrintHello()
  21. {
  22. Console.WriteLine("Hello");
  23. }
  24. }
  25. }

a[x] 元素访问操作符

访问数组元素:

  1. int[] myIntArray = new int[] { 1, 2, 3, 4, 5 };
  2. Console.WriteLine(myIntArray[0]);
  3. Console.WriteLine(myIntArray[myIntArray.Length - 1]);

索引字典中的元素:索引并不一定是整数

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Dictionary<string, Student> stuDic = new Dictionary<string, Student>();
  6. for (int i = 0; i < 100; i++)
  7. {
  8. var stu = new Student()
  9. {
  10. Name = "s_" + i.ToString(),
  11. Score = 100 - i
  12. };
  13. stuDic.Add(stu.Name, stu);
  14. }
  15. Console.WriteLine(stuDic["s_6"].Score);
  16. }
  17. }
  18. class Student
  19. {
  20. public string Name;
  21. public int Score;
  22. }

typeof 操作符

检测类型元数据(Metadata)。

  1. using System;
  2. using System.Collections.Generic;
  3. // Metadata
  4. var t = typeof(int);
  5. Console.WriteLine(t.Namespace);
  6. Console.WriteLine(t.FullName);
  7. Console.WriteLine(t.Name);
  8. int c = t.GetMethods().Length;
  9. Console.WriteLine(c);
  10. foreach (var m in t.GetMethods())
  11. {
  12. Console.WriteLine(m.Name);
  13. }

10 11 12 运算符详解 - 图2

default 操作符

  1. namespace OperatorsExample
  2. {
  3. class Program
  4. {
  5. static void Main(string[] args)
  6. {
  7. // 值类型内存块都刷成 0,值就是 0。
  8. int x = default(int);
  9. Console.WriteLine(x);
  10. // 引用类型内存块刷成 0,没有引用,default 是 null。
  11. Form myForm = default(Form);
  12. Console.WriteLine(myForm == null);
  13. // 枚举类型映射到整型上,默认枚举值是对应值为 0 的那个,可能是你手动指定的,也可能是系统默认赋值的。
  14. // 这就牵扯到我们使用枚举时,要注意枚举中是否有对应 0 的;创建枚举类型时,最好有一个对应 0 的,以免他人查找我们枚举的 default 值时报错。
  15. Level level = default(Level);
  16. Console.WriteLine(level);
  17. }
  18. }
  19. enum Level
  20. {
  21. Low = 1,
  22. Mid = 2,
  23. High = 0
  24. }
  25. }

new

  • new操作符
  • var操作符,隐式类型。必须在声明时利用初始化器给定某个确定的类型。
  • new:获取对象(实例)的地址,这时对象虽说没有赋值给某个变量,但此时依旧可以用点(.)操作符进行成员访问 ```csharp using System; using System.Windows.Forms;

namespace OperatorsExample3 { class Program { static void Main(string[] args) { new Form() //一会会被垃圾处理器销毁,一个失去控制的指针 { Text = “Hello” //访问对象的属性,修改窗口的文本 }.ShowDialog(); //访问对象的方法,打印窗口 } } }

  1. - 语法糖衣:
  2. ```csharp
  3. using System;
  4. using System.Windows.Forms;
  5. namespace OperatorsExample3
  6. {
  7. class Program
  8. {
  9. static void Main(string[] args)
  10. {
  11. int[] myArray = new int[10]; //利用new操作符实例化对象
  12. int[] MyArray; //使用语法糖衣声明对象
  13. }
  14. }
  15. }
  • 匿名类型

    1. // new 为匿名类型创建对象,并且用隐式类型变量(var)来引用这个实例
    2. var person = new { Name = "Mr.Okay", Age = true,Dob=true };
    3. Console.WriteLine(person.Name);
    4. Console.WriteLine(person.Age);
    5. Console.WriteLine(person.GetType().Name);

    10 11 12 运算符详解 - 图3
    10 11 12 运算符详解 - 图4

  • new关键字 ```csharp namespace OperatorsExample3 { class Program {

    1. static void Main(string[] args)
    2. {
    3. Student stu = new Student();
    4. stu.Report();
    5. CsStudent csStu = new CsStudent();
    6. csStu.Report();
    7. }

    }

    class Student {

    1. public void Report()
    2. {
    3. Console.WriteLine("Im a student");
    4. }

    }

    class CsStudent : Student {

    1. new public void Report() //new关键字,隐藏父类的同名方法
    2. {
    3. Console.WriteLine("Im Cs Student");
    4. }

    } }

  1. <a name="571415bd"></a>
  2. ### `checked` & `unchecked` 操作符
  3. - checked:检测溢出异常
  4. - 可以在单个语句内检测
  5. - 也可以检测一段语句
  6. - **concert**:将一种类型转化成另一种类型(例如:十进制转二进制)
  7. 目的:检查一个值在内存中是否有溢出
  8. 1. 未 check 时,`x+1`直接就溢出变成 0 了。
  9. ```csharp
  10. uint x = uint.MaxValue;
  11. Console.WriteLine(x);
  12. var binStr = Convert.ToString(x, 2);
  13. Console.WriteLine(binStr);
  14. uint y = x + 1;
  15. Console.WriteLine(y);

10 11 12 运算符详解 - 图5

2.单语句checked时

  1. uint x = uint.MaxValue;
  2. Console.WriteLine(x);
  3. var binStr = Convert.ToString(x, 2);
  4. Console.WriteLine(binStr);
  5. try
  6. {
  7. uint y = checked(x + 1);
  8. Console.WriteLine(y);
  9. }
  10. catch (OverflowException ex)
  11. {
  12. Console.WriteLine("There's overflow!");
  13. }

10 11 12 运算符详解 - 图6

3.unchecked,检测溢出不生效时

  1. try
  2. {
  3. // C# 默认采用的就是 unchecked 模式
  4. uint y = unchecked(x + 1);
  5. Console.WriteLine(y);
  6. }
  7. catch (OverflowException ex)
  8. {
  9. Console.WriteLine("There's overflow!");
  10. }

10 11 12 运算符详解 - 图7
4.checked 与 unchecked 的另一种用法,范围检测溢出。

  1. uint x = uint.MaxValue;
  2. Console.WriteLine(x);
  3. var binStr = Convert.ToString(x, 2);
  4. Console.WriteLine(binStr);
  5. //unchecked
  6. checked
  7. {
  8. try
  9. {
  10. uint y = x + 1;
  11. Console.WriteLine(y);
  12. }
  13. catch (OverflowException ex)
  14. {
  15. Console.WriteLine("There's overflow!");
  16. }
  17. }

10 11 12 运算符详解 - 图8

匿名方法(注册事件)

  • delegate声明匿名方法,注册点击事件

    1. public MainWindow()
    2. {
    3. InitializeComponent();
    4. this.myButton.Click += delegate(object sender, RoutedEventArgs e)
    5. {
    6. this.myTextBox.Text = "Hello World!";
    7. };
    8. }
  • Lamba表达式 声明匿名方法(New Get)

    1. public MainWindow()
    2. {
    3. InitializeComponent();
    4. this.myButton.Click += (sender,e)=>{this.myTextBox.Text = "Hello World!";};
    5. }

sizeof 操作符

sizeof 用于获取对象在内存中所占字节数。
注意:

  1. 默认情况下 sizeof 只能获取结构体类型的实例在内存中的字节数
    • int、uint、double 可以
    • string、object 不行
  2. 非默认情况下,可以使用 sizeof 获取自定义结构体类型的大小,但需要把它放在不安全的上下文中

需要在“项目属性”里面开启“允许不安全代码”。
10 11 12 运算符详解 - 图9

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. var x = sizeof(int);
  6. Console.WriteLine(x);
  7. unsafe
  8. {
  9. int y = sizeof(Student);
  10. Console.WriteLine(y);
  11. }
  12. }
  13. }
  14. struct Student
  15. {
  16. int ID;
  17. long Score;
  18. }

10 11 12 运算符详解 - 图10

注:int 字节数为 4,long 字节数为 8。但 sizeof(Student) 结果是 16。这涉及到了 .NET 对内存的管理,超出了现在所学内容。

-> 操作符

-> 操作符也必须放在不安全的上下文中才能使用。
C# 中指针操作、取地址操作、用指针访问成员的操作,只能用来操作结构体类型,不能用来操作引用类型。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. unsafe
  6. {
  7. Student stu;
  8. stu.ID = 1;
  9. // 用 . 直接访问
  10. stu.Score = 99;
  11. Student* pStu = &stu;
  12. // 用 -> 间接访问
  13. pStu->Score = 100;
  14. Console.WriteLine(stu.Score);
  15. }
  16. }
  17. }
  18. struct Student
  19. {
  20. public int ID;
  21. public long Score;
  22. }

检测空字符参数异常(取反字符)

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. var stu = new Student(null); //将null传入类字符参数,常规情况下应避免这种情况
  6. Console.WriteLine(stu.Name);
  7. }
  8. }
  9. class Student
  10. {
  11. public Student(string initName)
  12. {
  13. if (!string.IsNullOrEmpty(initName)) //检则这个参数是否是null,是就返回true,不是返回false
  14. {
  15. this.Name = initName;
  16. }
  17. else
  18. {
  19. throw new ArgumentException("initName cannot be null or empty.");
  20. } //异常显示函数,可以在运行中显示出哪里出的异常
  21. }
  22. public string Name;
  23. }

类型转换

  • 不丢失精度的转换
    • 隐式数值转换为:
      • 从 sbyte 到 short、int、long、float、double 或 decimal。
      • 从 byte 到 short、ushort、int、uint、long、ulong、float、double 或 decimal。
      • 从 short 到 int、long、float、double 或 decimal。
      • 从 ushort 到 int、uint、long、ulong、float、double 或 decimal。
      • 从 int 到 long、float、double 或 decimal。
      • 从 uint 到 long、ulong、float、double 或 decimal。
      • 从 long 到 float、double 或 decimal。
      • 从 ulong 到 float、double 或 decimal。
      • 从 char 到 ushort、int、uint、long、ulong、float、double 或 decimal。
      • 从 float 到 double。

从 int、uint、long 或 ulong 到 float 的转换以及从 long 或 ulong 到 double 的转换可能导致精度损失,但决不会影响数值大小。其他的隐式数值转换决不会丢失任何信息。
不存在向 char 类型的隐式转换,因此其他整型的值不会自动转换为 char 类型。

  • 子类向父类的转换

所有真正面向对象的语言都支持子类向父类转换。后面会讲到面向对象编程的一个核心概念 —— “多态”(polymorphism),多态就基于面向对象语言支持子类向父类的隐式转换。

  • 装箱
    • 可能丢失精度的转换
  • 显式(explicit)类型转换,即cast(铸造)

显式数值转换是指从一个 numeric-type 到另一个 numeric-type 的转换,此转换不能用已知的隐式数值转换(第 6.1.2 节)实现,它包括:

  1. - sbyte byteushortuintulong char。<br />
  2. - byte sbyte char。<br />
  3. - short sbytebyteushortuintulong char。<br />
  4. - ushort sbytebyteshort char。<br />
  5. - int sbytebyteshortushortuintulong char。<br />
  6. - uint sbytebyteshortushortint char。<br />
  7. - long sbytebyteshortushortintuintulong char。<br />
  8. - ulong sbytebyteshortushortintuintlong char。<br />
  9. - char sbytebyte short。<br />
  10. - float sbytebyteshortushortintuintlongulongchar decimal。<br />
  11. - double sbytebyteshortushortintuintlongulongcharfloat decimal。<br />
  12. - decimal sbytebyteshortushortintuintlongulongcharfloat double。<br />

显示类型转换还要特别注意有符号类型数据与无符号类型数据间的转换。有符号类型的最高位为符号位,如果其为负数(最高位为 1 ),将其转为无符号类型时必需注意。

  • Tostring方法与各数据类型的Parse/TryParse

    • Parse 只能解析格式正确的字符串数据类型。

      • 不正确的字符串类型:例如字母不能转换成数字,所以字母字符不能解析成数字
        1. double x = double.Parse(tb1.Text); //将tb1的text解析成double类型
    • TryParse 判断字符是否可以解析

      • 如果可以解析就是将输出结果解析给out参数。
  • 使用Convert类

    1. double x;
    2. if (double.TryParse(tb1.Text,out x)) //使用TryParse解析字符串
    3. {
    4. double y = Convert.ToDouble(tb2.Text); //使用Convert工具类进行类型转换
    5. double result = x + y;
    6. tb3.Text = result.ToString();
    7. }
  • 拆箱

    • 自定义类型转换操作符

**
示例:让石头类支持显式转换为猴子。

  1. class Program
  2. {
  3. static void Main(string[] args)
  4. {
  5. Stone stone = new Stone();
  6. stone.Age = 5000;
  7. Monkey wukongSun = (Monkey)stone; //使用显式类型转换
  8. //Monkey wukongSun = stone; //使用隐式类型转换
  9. Console.WriteLine(wukongSun.Age);
  10. }
  11. }
  12. class Stone
  13. {
  14. public int Age;
  15. // 转换器写在被转换类型里面
  16. public static explicit operator Monkey(Stone stone) //声明显示类型转换
  17. //public static implicit operator Monkey(Stone stone) //声明隐式类型转换
  18. {
  19. Monkey m = new Monkey();
  20. m.Age = stone.Age / 500;
  21. return m;
  22. }
  23. }
  24. class Monkey
  25. {
  26. public int Age;
  27. }

is 操作符

  • 判断一个引用类型的变量的 是不是 某个引用类型
  1. Teacher t = new Teacher();
  2. // 检测 t 所引用的实例是否为 Teacher
  3. var result = t is Teacher;
  4. Console.WriteLine(result.GetType().FullName);
  5. Console.WriteLine(result);
  6. Console.WriteLine(t is Animal);
  7. Car car = new Car();
  8. Console.WriteLine(car is Animal);
  9. Console.WriteLine(car is object);
  10. Human h = new Human();
  11. Console.WriteLine(h is Teacher);

10 11 12 运算符详解 - 图11

as 操作符

  1. object o = new Teacher();
  2. //if(o is Teacher)
  3. //{
  4. // var t = (Teacher)o;
  5. // t.Teach();
  6. //}
  7. Teacher t = o as Teacher; //判断o想不想teacher,如果否就返回null,如果真就返回o
  8. if (t != null)
  9. {
  10. t.Teach();
  11. }

?? null 合并操作符

  1. //Nullable<int> x = null;
  2. int? x = null;
  3. Console.WriteLine(x.HasValue);
  4. // x 如果为 null,就拿 1 来代替。
  5. int y = x ?? 1;
  6. Console.WriteLine(y);

?: 条件操作符

唯一一个三元操作符,本质上就是 if else 的简写。

  1. int x = 80;
  2. // 使用 () 将条件括起来,提高可读性。
  3. string str = (x >= 60) ? "Pass" : "Failed"; //x大于等于60吗,如果大于返回pass,如果不大于,返回Failed
  4. Console.WriteLine(str);