C#学习笔记5
开发环境:vs2022
1. 操作符的本质
- 操作符的本质是函数(即算法)的“简记法”
- 操作符不能脱离与他关联的数据类型
- 可以说操作符就是与固定数据类型相关联的一套基本算法的简记法
- 示例:为自定义数据类型创建操作符
internal class Program { static void Main(string[] args) { Person person1 = new Person(); Person person2 = new Person(); person1.Name = "Deer"; person2.Name = "Deer's wife"; //List<Person> nation = Person.GetMarry(person1, person2); List<Person> nation = person1 + person2; //使用这一操作符 foreach (var p in nation) { Console.WriteLine(p.Name); } } } class Person { public string Name; //public static List<Person> GetMarry(Person p1, Person p2) //泛型 public static List<Person> operator +(Person p1, Person p2) //把GetMarry这一方法定义为特定数据类型的操作符 { List<Person> people = new List<Person>(); people.Add(p1); //Add方法将往List集合末尾添加相应元素对象 people.Add(p2); for(int i = 0; i < 11; i++) { Person child = new Person(); child.Name = p1.Name + "&" + p2.Name + "'s child"; people.Add(child); } return people; } }
2. c#中Add方法将往List集合末尾添加相应元素对象
List<Person> people = new List<Person>();
people.Add(p1); //Add方法将往List集合末尾添加相应元素对象
people.Add(p2);
3. 优先级与运算顺序
- 操作符的优先级
- 可以使用圆括号提高被括起来表达式的优先级
- 圆括号可以嵌套
- 不像数学里有方括号和花括号,在C#语言里“[]”与“{}”有专门的用途
- 同优先级操作符的运算顺序
- 除了带有赋值功能的操作符,同优先级操作符都是由左向右进行运算
- 带有赋值功能的操作符的运算顺序是由右向左
- 与数学运算不同,计算机语言的同优先级运算没有“结合率”,3+4+5只能理解为Add(Add(3, 4), 5)不能理解为Add(3, Add(4, 5))
4. 操作符详解
-
-
成员访问操作符:x.y
- 用成员访问操作符来访问外层名称空间中的子集名称空间
System.IO
- 用成员访问操作符来访问名称空间中的类型
System.IO.File
- 用成员访问操作符来访问类型的静态成员
System.IO.File.Create(args[0]);
- 用成员访问操作符来访问实例里面的实例成员
Form myForm = new Form(); myForm.Text = "Hello world";
- 用成员访问操作符来访问实例里面的方法
myForm.ShowDialog();
-
调用操作符:f(x)
- 用调用操作符来调用方法
- 也是实例构造器
internal class Program { static void Main(string[] args) { Calculator c = new Calculator(); double x = c.Add(3.0, 5.0); Console.WriteLine(x); } } class Calculator { public double Add(double a, double b) { return a + b; } }
-
索引器操作符:a[i]
- 方括号 [] 通常用于数组、索引器或指针元素访问
- 括号中的索引不一定都是整数(后面学
- 数组的示例
//创建数组实例 int[] myIntArray = new int[10];
-
初始化器操作符:{}
- 数组的示例
int[] myIntArray = new int[] { 1, 2, 3, 4, 5 };
-
后缀增量操作符:x++
- 先使用后加1
- 示例
int x = 100; int y = x++; Console.WriteLine(x); //101 Console.WriteLine(y); //100
-
后缀递减操作符:x–
与上面原理类似 -
typeof操作符
- 用于获取某个类型的 System.Type 实例
- 示例
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);
结果如下
-
default 运算符
- 生成类型的默认值
- 结构体类型的defoult值是0;引用类型的default值是NUll
- 示例
double x = default(double); //结构体类型 Console.WriteLine(x); //0 Form myForm = default(Form); //引用类型 Console.WriteLine(myForm==null); //ture
- 枚举类型的第一个元素为0,故默认打印第一个元素
internal class Program { static void Main(string[] args) { Level level = default(Level); //枚举类型的默认值 Console.WriteLine(level); //Low } enum Level { Low, Mid, High } }
- 如果枚举类型传创建时自己赋值,且没有0,就可能会发生错误
-
new操作符 (当作操作符来使用)
- var关键字:声明隐式类型的变量,由它自己判断变量的类型。
int x = 100; //显式 var y = 100; //隐式 Console.WriteLine(y.GetType().Name); //Int32 var m = 100L; //隐式 Console.WriteLine(m.GetType().Name); //Int64 var n = 100D; //隐式 Console.WriteLine(n.GetType().Name); //Double
- c#是强类型语言,一旦确定变量的类型后,在后面的赋值中不能将他改为其他类型
int x= 100; x = "100"; //这里会报错
错误 CS0029 无法将类型“string”隐式转换为“int”
- new操作符的主要功能:在内存中创建类型的实例,并且立刻调用其实例构造器,如果在new操作符左边有赋值符号的话,new将拿到的实例的地址赋给前面的访问这个实例的变量。
//创建实例Form,调用其实例构造器() new Form();
//创建实例Form,调用其实例构造器(),将实例的地址赋值给引用变量myForm Form myForm = new Form();
- new操作符的附加功能:调用实例的初始化器,可以初始化多个属性
Form myForm = new Form() { Text = "Hello", FormBorderStyle = FormBorderStyle.SizableToolWindow }; myForm.ShowDialog();
标题,以及上方工具栏隐藏- 不使用引用变量,去初始化。不使用引用变量,只能执行一下,后面就被垃圾收集器回收。
new Form() { Text = "Hello" }.ShowDialog();
- 语法糖衣
类类型创建实例的时候要使用new操作符,string也是类型型,但是它不用调用new操作符,是因为c#语法将new隐藏了。s数组也有类似的用法。
string name = "Tim"; int[] myArray = new int[10]; int[] myArray1 = {1,2,3};
- 匿名类型
Form myForm = new Form(); //非匿名类型,名字Form var person = new { Name = "Mr.Okay", Age = 34 }; //匿名类型,new后面直接跟初始化为匿名类型创建实例,使用var去定义类型 Console.WriteLine(person.Name); //显示Mr.Okay Console.WriteLine(person.Age); //34
此处var关键字的强大之处就体现出来了,因为这个类型都不知道是啥类型
- new操作符不可以滥用,new操作符会和上面的类紧耦合,在编写大型程序时,有一种依赖注入模式会把这种紧耦合变成松耦合。
-
checked和unchecked操作符
- 在 c# 中,checked 和 unchecked 是用于控制整数运算溢出检查的关键字。它们允许我们明确指定在进行整数运算时是否要检查溢出,以及如何处理溢出情况。
- 在c#中,默认使用unchecked
uint x = uint.MaxValue; //获取uint能表示的最大值 Console.WriteLine(x); string binStr = Convert.ToString(x, 2); //转换成二进制 Console.WriteLine(binStr); uint y = checked(x + 1); //报错:未经处理的异常: System.OverflowException: 算术运算导致溢出。 Console.WriteLine(y);
- 使用 try-catch 语句处理在执行代码块期间可能发生的异常。 将代码置于 try 块中可能发生异常的位置。catch语句处理发生的异常
- 操作符形式的用法
//输出There's overflow!,checked检测溢出 try { uint y = checked(x + 1); Console.WriteLine(y); } catch (OverflowException) { Console.WriteLine("There's overflow!"); } //输出0,unchecked不检测溢出 try { uint y = unchecked(x + 1); Console.WriteLine(y); } catch (OverflowException) { Console.WriteLine("There's overflow!"); }
- 上下文形式的用法
checked { try//使用 try-catch 语句处理在执行代码块期间可能发生的异常。 将代码置于 try 块中可能发生异常的位置。catch语句处理发生的异常 { uint y = x + 1; Console.WriteLine(y); } catch (OverflowException) { Console.WriteLine("There's overflow!"); } }
-
sizeof操作符
- 使用sizeof操作符来获取基本类型在内存中所占的字节数,只能结构体类型
int a = sizeof(int); Console.WriteLine(a); //4 int b = sizeof(double); Console.WriteLine(b); //8
- sizeof操作符可以在unsafe上下文中获取自定义结构体的内存大小
static void Main(string[] args) { unsafe { int c = sizeof(Student); Console.WriteLine(c); //16 } struct Student { int ID; long Score; }
unsafe要在项目——属性——生成——勾选“允许不安全代码”
-
->取地址操作符 和 &x解引用操作符
- 作用与c语言中的一样
- 在c#中需要在unsafe上下文中使用
- 只能操作结构体类型,不能操作引用类型
static void Main(string[] args) { unsafe { Student stu; stu.ID = 1; stu.Score = 99; Student* pStu = &stu; pStu->Score = 100; Console.WriteLine(stu.Score); (*pStu).Score = 1000; Console.WriteLine(stu.Score); } } struct Student { public int ID; public long Score; }
-
+,-,~操作符
- 常规加减用法与c语言一样
- 对整型操作,如果是对字符串类型操作,则是把两个字符串接在一起
- 负号取反取反要加括号
int m = 100; int n = -m; Console.WriteLine(n); //-100 n = -(-m); Console.WriteLine(n); //100
- 用负号取反是对二进制每一位取反加1,用~取反只对每一位取反不加1
- PadLeft函数是向左补齐32位,用0补齐
int m = 1234567; int n = -m; //int n = ~m; string mStr = Convert.ToString(m, 2).PadLeft(32, '0'); string nStr = Convert.ToString(n, 2).PadLeft(32, '0'); Console.WriteLine(mStr); //00000000000100101101011010000111 Console.WriteLine(nStr); //11111111111011010010100101111001
-
取非操作符!
- 对布尔类型取非
bool b1 = false; bool b2 = !b1; Console.WriteLine(b2);
- 例子
internal class Program { static void Main(string[] args) { Student1 stu1 = new Student1(null); Console.WriteLine(stu1.Name); } } class Student1 { public Student1(string initName) { if(!string.IsNullOrEmpty(initName)) //如果名字非空则使用 { this.Name = initName; } else { throw new ArgumentException("initName cannot be null or empty!"); //名字空则报错 } } public string Name; }
- throw的用法看大佬文章:链接: link
-
++x操作符 和 --x操作符
- 先自增(自减)再使用
-
类型转换操作符(T)x
-
隐式类型(implicit)转换
- 不丢失精度的转换
namespace ConversionExample { internal class Program { static void Main(string[] args) { //不丢失精度的转换 int x = int.MaxValue; //获取int类型的最大值 long y = x; //转换 Console.WriteLine(y); //2147483647 } } }
- 子类向父类的转换
多的往少转换;
继承用 " : "
namespace ConversionExample { //子类向父类的转换(多的转成少的) internal class Program { static void Main(string[] args) { Teacher t = new Teacher(); Human h = t; Animal a = h; a.Eat(); //多的转换成少的 } } class Animal { public void Eat() { Console.WriteLine("Eating..."); } } class Human : Animal //将Animal类作为该类的基类(父类),继承Animal类的方法 //Human派生自Animal { public void Think() { Console.WriteLine("Who I am?"); } } class Teacher : Human //将Animal类和Human作为该类的基类(父类) { public void Teach() { Console.WriteLine("I teach class."); } } }
- 装箱
-
显式(explicit)类型转换
- 有可能丢失精度(甚至发生错误)的转换,即cast
namespace ConversionExample { internal class Program { static void Main(string[] args) { Console.WriteLine(ushort.MaxValue); //65535 uint x = 65536; ushort y = (ushort)x; //直接ushort y = x;会报错 Console.WriteLine(y); //0 } } }
-
拆箱
-
使用Convert类
-
ToString方法与各数据类型的Parse/TryParse方法
使用WPF窗体
private void Btn_click(object sender, RoutedEventArgs e) { double x = System.Convert.ToDouble(tb1.Text); double y = System.Convert.ToDouble(tb2.Text); double result = x + y; //this.tb3.Text=System.Convert.ToString(result); //使用Convert this.tb3.Text = result.ToString(); //直接使用ToString() }
Parse与TryParseprivate void Btn_click(object sender, RoutedEventArgs e) { double x = double.Parse(tb1.Text); double y = double.Parse(tb2.Text); double result = x + y; this.tb3.Text = result.ToString(); //直接使用ToString() }
注意输入格式的正确性
上面这种出不来结果 -
自定义类型转换操作符示例
本质是在被转换的类中创建方法
特定的关键字
隐式和显式的区别
internal 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); } } class Stone //如果里面不加任何操作,上面强制类型转换,无法转换 { public int Age; //该实例创建在被转换的类里面 public static explicit operator Monkey(Stone stone) //public static implicit operator Monkey(Stone stone) //隐式类型转换 { Monkey m = new Monkey(); m.Age = stone.Age / 500; return m; } } class Monkey { public int Age; }
-
-
+, -, “*”, /, %操作符
- 留意数值提升,例如:int->double
- 正无穷,负无穷,NaN等看文档
- 操作的类型不同,进行不同的运算。例如:整数除法与浮点型除法
- 正无穷与负无穷
double x = 5.0; double y = 0; double z = x / y; //+Infinity double x = -5.0; double y = 0; double z = x / y; //-Infinity double x = double.PositiveInfinity; double y = double.NegativeInfinity; double z = x / y; //NaN(Not a Number)
- 加减法也会发生类型提升
var x = 3.0 + 4; Console.WriteLine(x.GetType().Name); Console.WriteLine(x);
- 加号连接字符串
string s1 = "123"; string s2 = "abc"; string s3 = s1 + s2; Console.WriteLine(s3); //123abc
-
位移操作符 << 和 >>
- 注意溢出的情况
- 在没有溢出的情况下,左移就是乘2右移就是除2
- 左移和右移的补位规则。左移不论是正数还是负数最高位都死补0,右移正数最高位补0,负数最高位补1
-
关系操作符 > < >= <= == !=
- 关系操作符的运算结果是布尔类型的
- 可以比较字符类型与数据类型
- 字符串只能比相等于不相等,不能比大小
- 大写字母转小写字母库里面的方法:Tolower()
string str1 = "abc"; string str2 = "Abc"; Console.WriteLine(str1.ToLower() == str2.ToLower()); //大写字母转成小写字母
-
类型检验操作符is as
- is操作符用来检验一个对象是不是某个类型的对象
- 检验的不是变量而是变量所引用的实例
- is与as的更多用法参考大佬文章:链接: link
-
逻辑运算符& | ^ && ||
- 基本用法简单
- 使用&&和||时要注意:从左往右执行时后面的条件是否还会执行?(短路效应),注意避开这一特点
-
null合并操作符 ??
- c#将Nullable关键字吸收为“ ?”
//Nullable<int> x = null; //使用该关键字将null放入int类型当中 int? x = null; Console.WriteLine(x); //没东西 Console.WriteLine(x.HasValue); //False
- ??引用实例介绍
int? x = null; int y = x ?? 1; //第一个问号判断x是否为空,如果为空则赋值1 Console.WriteLine(y); //1
-
其他操作符常规,不做笔记
出处链接: link