操作符概述
- 操作符(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
class Person
{
public string Name;
//将operator +替代GetMarry
public static List<Person> operator +(Person p1, Person p2)
{
List<Person> people = new List<Person>();
people.Add(p1);
people.Add(p2);
for (int i = 0; i < 11; i++)
{
Person child = new Person();
child.Name = p1.Name + "&" + p2.Name + "'s child" + (i+1);
people.Add(child);
}
return people;
}
}
}
结果如下:<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)类型转换
- 不丢失精度的转换
- 子类向父类的转换
- 装箱
- 示例:不丢失精度,子类向父类的转换 ```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 }