C#.Net筑基-运算符🔣Family
C#运算符 内置了丰富的运算符操作类型,使用方便,极大的简化了编码,同时还支持多种运算符重载机制,让自定义的类型也能支持运算符行为。
01、运算符概览
运算符分类 | 描述 |
---|---|
数学运算 | 基础的加减乘除,及++、-- |
赋值运算 | =,及各种复合赋值op= ,x+=20; 等效于x=x+20; |
比较运算 | 比较相等、大小,内置类型大多支持,自定类型需要自己重载运算符才能支持 |
逻辑运算符 | 常用的就是非! 、短路逻辑与&& 、短路逻辑或 ||。 |
位运算 | 二进制位运算,适当使用可极大提高数据处理性能 |
类型相关运算符 | 类型判断is 、类型转换as 、typeof ... |
指针操作运算符 | 指针相关运算符:*、&、-> |
其他运算符 | ^ 、.. 范围运算、nameof 、default (默认值)、await /async ... |
运算符重载 | public static Point operator +(Point p1, Point p2) {} |
隐式转换 | public static implicit operator int(Point p1){} |
显示转换 | public static explicit operator string(Point p){} |
02、运算符汇总
2.1、算数运算
运算符 | 描述 | 示例 |
---|---|---|
+ | 加,字符串加为字符串连接,推荐字符内插$"{var}" |
|
- | 减,同+ 可用于委托,也可表示负数-100 |
|
* | 乘 | |
/ | 除 | |
% | 取余数 | 6%3; //=0 |
++ | 自加1,符号在前面的先运算,再赋值,在后面的反之 | x=++y; //y=1+y; x=y; x=y++; //x=y; y=1+y; |
-- | 自减1,同上 |
- 整数除以0会引发 DivideByZeroException,浮点数不会,结果为无穷
∞/Infinity
。 - 整数运算超出范围,默认情况下不会引发异常,会被自动截断(循环),除非使用检查语句(checked),此时超出范围会引发OverflowException。而常量表达式默认检查。
- 浮点数
float
和double
不会出现超出范围异常问题。 - decimal 类型溢出始终会已发异常 OverflowException,被零除总是引发 DivideByZeroException。
- 整数除以整数,结果依然是整数,向下(截断)取整。
int a = 3;
int b = a +int.MaxValue; //-2147483646
int c = checked(a + int.MaxValue); //System.OverflowException
2.2、赋值运算
运算符 | 描述 | 示例/备注 |
---|---|---|
= | 赋值运算符,一个神圣的仪式 | |
+= | 加法赋值 | x+=y //x=x+y |
-= | 减法赋值 | x-=y //x=x-y |
*= | 乘法赋值 | x*=y //x=x*y |
/= | 除法赋值 | x/=y //x=x/y |
%= | 取余赋值 | a%=3 //a=a%3 |
op= | 复合赋值x op = y = x = x op y |
上面这些都是复合赋值 |
= ref | 按引用赋值,可看做是别名,是同一个地址的别名 | ref int b = ref a; |
?? ??= |
Null 合并赋值,为null 时就用后面的表达式值。 ??= 可用来设置默认值,或为null 时抛出异常 |
c = b1 ?? 5; name??= "sam"; _= sender ??throw... |
2.3、比较运算
运算符 | 描述 | 示例/备注 |
---|---|---|
== | 相等比较,值类型比较值,应用类型比较地址 | if(x == 0){} |
!= | != 不等于比较,与== 对应 |
if(x != 1){} |
>,< | 大于,小于 | |
>=,<= | 大于等于,小于等于 |
对于相等(==、!=)比较:
- 值类型比较的是数据值是否相等。
- 引用类型
==
比较的是其引用地址(对象),同 Object.ReferenceEquals。 - 字符串比较的字符串值,虽然他是引用类型(一个特殊的引用类型)。
- 用户定义的 struct 类型默认情况下不支持 == 运算符,需要自己重载运算符。可以用
Equals()
方法,会box装箱。 - C# 9.0 中的值类型 记录类型
record struct
支持 == 和 != 运算符,比较的是其内部值。这是因为record
内部实现了相等的运算符重载,比较了每个属性的值。 - 委托,如果其内部列表长度相同,且每个位置的元素相同则二者相等。
对于大小比较:
- char 比较的是字符码值。
2.4、逻辑运算符
运算符 | 描述 | 示例/备注 |
---|---|---|
! | ! 逻辑非运算符,取反,颠掉黑白! |
!false //true |
& | 逻辑与,都为true 结果为true ,都会计算,前面false 还是会计算后面的 |
true & false //false |
^ | 逻辑异或,不同则为ture ,相同false |
true ^ false //true |
| | 逻辑或,只要有一个true ,则为true ,都会计算 |
true | false //true |
&& | 条件逻辑与,也叫短路运算符,都为true 才为true ,先遇到false 就返回了 |
效果同& ,推荐用&& |
|| | 条件逻辑或,也是短路的,只要有true 就返回true ,先遇到true 就返回了 |
效果同"|",推荐用"||" |
?: | 三元表达式:判断条件 ?条件为真 :条件为假 |
age = a > 2 ? 4 : 2 |
- 上面运算符除了
&&
、||
都支持可空类型的bool?
,只是结果可能为null
。 - 一般逻辑与、逻辑或都用支持短路的
&&
、||
,可提前返回,减少运算。
void Main()
{
Console.WriteLine(F()&T()); // f t False 都运算了
Console.WriteLine(F()&&T()); // f False 发现fasle就返回了,后面就不运算了
Console.WriteLine(T()|F()); // t f True 都运算了
Console.WriteLine(T()||F()); // t True 发现true就返回true了,后面就不运算了
Console.WriteLine(true^true); // False
Console.WriteLine(true^false); // True 不同则为true
Console.WriteLine(false^false); // False
}
bool T()
{
Console.Write("t ");
return true;
}
bool F()
{
Console.Write("f ");
return false;
}
2.5、位运算
运算符 | 描述 | 示例/备注 |
---|---|---|
~ | 按位求补,每位(所有位)反转 申明终结器(析构函数), ~className(){} |
uint b = ~a; |
<< | 左移位,向左移动右侧操作数定义的位数 | uint y = 1 << 5; ,//32(1*2的5次方) |
>> | 右移位,向右移动右侧操作数定义的位数 | 8>>2 //2(8除以2的2次方) |
>>> | 无符号右移 | 0b100>>>2 //1 |
& | 逻辑与,计算每一位的逻辑与 | 0b100 & 0b111 //100 |
| | 逻辑或,计算每一位的逻辑或 | 0b100 | 0b111 //111 |
^ | 逻辑异或,计算每一位的逻辑异或 | 0b100 ^ 0b111 //11 |
左移位
<< n
,相当于乘以2的n次方,同样的,右移位意味着除以2的n次方。移位运算比乘法、除法运算效率要高很多!所以在合适的场景可考虑使用位运算。
Console.WriteLine(123456*Math.Pow(2,6)); //7901184
Console.WriteLine(123456<<6); //7901184
用左移位给Flags
枚举赋值:
[Flags]
public enum Interest
{
None = 0, //0
Study = 1, //1
Singing = 1 << 1, //2
Dancing = 1 << 2, //4
Sports = 1 << 3, //8
Games = 1 << 4, //16
}
2.6、类型、成员运算符
运算符 | 描述 | 示例/备注 |
---|---|---|
Object. prop |
成员访问表达式,命名空间、类、成员访问 | user.name |
?. 、?[] |
Null 条件运算符,仅非null 时才继续,否则返回null |
简化null 判断,arr?[1]??1 |
new | new Object() 创建对象实例,C#9可省略后面的类型。重新实现父类的方法、属性。 创建匿名实例 new {n=1} 。 |
User u1 = new(); var a = new {Name="sam"} |
is (E is T) |
类型兼容性检查,如果兼容则为True,否则false。支持可空泛型、装箱的兼容转换。is 支持各种模式匹配,参考《C#模式匹配》 |
if(obj is int i) //True |
as (E as T) |
显式转换类型,如果转换失败则返回null ,不会报错。只支持引用类型、可空类型、装箱拆箱。 |
int b =i as User |
(T)E | 强制类型转换,如果不匹配会引发异常 | var b = (int)b1 |
typeof |
获取类型System.Type,参数只能是类型、T,不支持dynamic、string?,可用Object.GetType代替。 | typeof(int?) b1?.GetType() |
sizeof |
获取类型所需大小(字节数),只能用于值类型、不安全类型 | sizeof(int) //4 |
() |
调用表达式,调用方法,执行委托/方法,new构造函数。 创建一个Tuple 强制类型转换 (Type)v ,转换失败抛出异常 |
Foo(); var b = (1,2,"ss"); var b = (int)a |
[] |
数组下标,必须为整数 索引器,可任意类型,如Dictionary <TKey,TValue> 特性Attribute的使用 |
arr[0] =1; dict["one"] = 1; [Serializable] |
2.7、其他运算符
运算符 | 描述 | 示例/备注 |
---|---|---|
[value]! |
在表达式后面,告诉编译器不会为空,不要在提示告警了 | p.Message!.Length |
^ |
范围运算符,从末尾开始索引,倒数 | arr[^1] // 倒数第一个 |
.. |
范围运算符,.NET Standard 2.1/C#8,表达式 a..b 属于 System.Range 类型,表示a 到b 的范围。a.. 等效于 a..^0 |
arr[1..3] // 索引1到3的值 |
with |
C#9非破坏性创建+修改,创建一个副本,并修改特定属性,只支持record 、struct 类型。 |
n = o with { age = 2 } |
nameof |
获取变量、类型、成员的名称作为字符串常量,编译时获取 | nameof(numbers.Add) |
stackalloc |
堆栈上分配内存块,用于System.Span<T> 、unsafe 代码 |
arr = stackalloc int[10] |
(p) => {} |
Lambda表达式创建匿名函数,左侧为参数,右侧为表达式 | var f = ()=>x=100; |
default | 默认值,获取类型的默认值,T initialValue = default 数值类型默认0、false,引用类型默认null |
default(int) //0 int x = default; |
:: |
命名空间别名的使用,using t = System.Text; t::Json. |
|
await、async | 异步编程,async 标记方法为异步方法 |
|
&、* | 指针操作:* 获取指针指向的变量,& 获取变量地址 int n = 100; int* n1 = &n; int n2 = *n1; |
在unsafe 上下文中运行 |
03、运算符优先级
运算符优先级,最简单的方法就是加括号()
。
04、operator 运算符重载
上面这么多运算符,有些是可以自定义重载实现的,让自定义的类型(class、struct)也支持运算符操作,比如让自定的类型支持相加、相减、大小比较。
void Main()
{
var p1 =new Point(1,2);
var p2 =new Point(3,4);
var p = p1+p2; //4,6
p +=p; //8,12
}
public class Point //struct也是一样的
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public static Point operator +(Point p1, Point p2)
{
return new Point(p1.X + p2.X, p1.Y + p2.Y);
}
}
上面的示例是重载实现+
运算符,实现运算符重载的基本语法:public static T operator +(T arg)
- 方法必须是
public
+static
的,然后operator
关键字,“方法名”就是运算符,后面就是正常的方法参数。 - 所以不难看出重载的运算符本质上就是静态方法调用,下面是上面示例的
IL
代码。
4.1、可重载的运算符
运算符 | 说明 |
---|---|
+x, -x, !x, ~x, ++, --, true, false | true和 false 运算符必须一起重载(比较少见)。 |
x + y, x - y, x * y, x / y, x % y, | |
x & y, x | y, x ^ y, | |
x << y, x >> y, x >>> y | |
x == y, x != y, x < y, x > y, x <= y, x >= y | 必须按如下方式成对重载: == 和 !=、 < 、 ><= 和 >= |
还有些不可重载运算符
- 成对重载:有些运算符的重载要求需成对实现,如
==
和!=
、<
和>
、<=
和>=
。 - Equals 和 GetHashCode:如果重载了相等运算符,则也应该重载Equals、GetHashCode方法才更完整、合理。
- IComparable:如果重载了大小比较运算符,则应当实现
IComparable
接口。
4.2、显式和隐式转换运算
隐式转换 顾名思义就是自动匹配类型并完成转换,显示类型转换就是要指定转换的类型T(T)value
。
- 隐式转换多用于无损、必定转换成功的场景,如
int
转double
、long
,short
转换为int
。 - 强制转换则可能存在信息损失,或转换可能存在不确定性,如上面示例的相反方向转换。
void Main()
{
int a =100;
float f = a; //int隐式转换为了float
char c1 = a; //报错,不支持隐式转换
char c2 =(char)a; //强制转换
Console.WriteLine(c2); //d
}
自定义实现隐式、显示类型转换主要是下面两个关键字:
- implicit (/ɪmˈplɪsɪt/ 隐式),隐式类型转换运算,用于无损数据类型转换,一般用于“小”数据类型转换为“大”数据类型,如
int
转换为long
、float
。 - explicit (/ɪkˈsplɪsɪt/ 显示),显示类型转换运算,可能会导致数据丢失的转换。
void Main()
{
Point p1 = 2; //2,2
int a = p1; //2
string pstr = (string)p1; //(2,2)
}
public class Point
{
public int X { get; set; }
public int Y { get; set; }
public Point(int x, int y)
{
X = x;
Y = y;
}
public static implicit operator int(Point p1) //Point隐式转换为int
{
return (p1.X + p1.Y) / 2;
}
public static implicit operator Point(int v) //int隐式转换为Point
{
return new Point(v, v);
}
public static explicit operator string(Point p)//Point显示转换为string
{
return $"({p.X},{p.Y})";
}
}
一般建议强相关的类型可实现显示、隐式转换,对于弱相关类型的转换则建议编写专门的转换函数,如果
ToXXX
,Parse
方法。
参考资料
️版权申明:版权所有@安木夕,本文内容仅供学习,欢迎指正、交流,转载请注明出处!原文编辑地址-语雀
C#.Net筑基-运算符🔣Family的更多相关文章
- JavaScript -- 筑基
本片博客记录了我JavaScript筑基阶段的学习内容,JavaScript不是Java,但是我下意识的把它和java对比学习,有些地方比较有趣,有些地方从java角度看,简直匪夷所思,不过现在总体感 ...
- shell运算符
原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如 awk 和 expr,expr 最常用. expr 是一款表达式计算工具,使用它能完成表达式的求值操作. #!/bin/bash v ...
- SQL Server-聚焦APPLY运算符(二十七)
前言 其实有些新的特性在SQL Server早就已经出现过,但是若非系统的去学习数据库你会发现在实际项目中别人的SQL其实是比较复杂的,其实利用新的SQL Server语法会更加方便和简洁,从本节开始 ...
- java中的移位运算符:<<,>>,>>>总结
java中有三种移位运算符 << : 左移运算符,num << 1,相当于num乘以2 >> : 右移运算符,num >& ...
- 我的MYSQL学习心得(五) 运算符
我的MYSQL学习心得(五) 运算符 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据 ...
- .NET 基础 一步步 一幕幕[运算符、占位符、转义符]
运算符.占位符.转义符 好吧,在五局全胜之后,终于升到了三个钻,距离一个星星还有一大段距离,忽然想起来今天的博客还没写,果断坑队友,来写博客了....感觉以后还是每天更新一篇比较好.要不晚上就该熬 ...
- ECMASCript2015 提案 stage-3的对象展开运算符
看源码时看到如下的代码 export default { //通过mapActions将actions映射到methods里 methods: { ...mapActions([ 'updateSta ...
- JavaScript权威设计--JavaScript表达式与运算符,语句(简要学习笔记六)
1.delete是一元操作符,用来删除对象属性或者元素. var a={ x:1, y:2 } delete a.x; //删除x属性 “x”in a //false:a对象中已经不存在x属性 ale ...
- JavaScript权威设计--JavaScript表达式与运算符(简要学习笔记五)
1.3种原始表达式 1.直接量: 1.23 //数字直接量 “hello” //字符串直接量 ...
- LINQ to SQL语句(18)之运算符转换
运算符转换 1.AsEnumerable:将类型转换为泛型 IEnumerable 使用 AsEnumerable<TSource> 可返回类型化为泛型 IEnumerable 的参数.在 ...
随机推荐
- 学习Source Generators之从swagger中生成类
前面学习了一些Source Generators的基础只是,接下来就来实践一下,用这个来生成我们所需要的代码. 本文将通过读取swagger.json的内容,解析并生成对应的请求响应类的代码. 创建项 ...
- 微服务集成Spring Cloud Alibaba Seata (二) 客户端连接
通过上篇文章后我们的Seata服务就部署成功了,如果还不清楚怎么部署或者还没有部署Seata服务的朋友可以看我写的上篇文章进行服务部署.Seata部署步骤:https://www.cnblogs.co ...
- 数据解析之re正则表达式
数据解析之re正则表达式 1. 正则基础 1.1 为什么使用正则 需求 判断一个字符串是否是手机号 解决 编写一个函数,给函数一个字符串,如果是手机号则返回True,否则返回False 代码 def ...
- Avalonia下拉可搜索树(TreeComboBox)
1.需求分析 树形下拉的功能是ComboBox和TreeView的功能结合起来,再结合数据模板来实现这一功能. 2.代码实现 1.创建UserControl集成TreeView控件 2.将 ...
- 3.1版本【HarmonyOS 第一课】正式上线!参与学习赢官方好礼>>
[课程介绍] <HarmonyOS第一课>是跟随版本迭代不断推出的系列化课程,本期课程基于HarmonyOS 3.1版本的新技术和特性,每个课程单元里面都包含视频.Codelab.文章 ...
- Linux&Ubuntu之更换服务器
前言 更换网卡.主板上的板载网卡.主板是服务器硬件维护的常规操作.通常新换(板载)网卡的MAC地址会变更,而部分服务器更换主板也会导致板载网卡MAC地址变化.由于CAS(Ubuntu)系统会将新MAC ...
- MMDeploy部署实战系列【第二章】:mmdeploy安装及环境搭建
MMDeploy部署实战系列[第二章]:mmdeploy安装及环境搭建 这个系列是一个随笔,是我走过的一些路,有些地方可能不太完善.如果有那个地方没看懂,评论区问就可以,我给补充. 版权声明:本文为博 ...
- golang开发 深入理解 context
context的历史 context包在Go 1.7版本正式加入Go标准库.在加入之前我们看看Go团队核心成员Sameer Ajmani在2014年发表的一篇关于context介绍博客,地址:http ...
- SVM简单分类的使用 sklearn机器学习
# sklearn 库中导入 svm 模块 from sklearn import svm # 定义三个点和标签 X = [[2, 0], [1, 1], [2,3]] y = [0, 0, 1] # ...
- 力扣33(java&python)-搜索旋转排序数组(中等)
题目: 整数数组 nums 按升序排列,数组中的值 互不相同 . 在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 ...