操作符(详细)
目录
优先顺序由上到下,同一级的由左到右,但是最后的赋值和lambda表达式则从右到左。
操作符是函数的简计法,操作符其实就是函数
operator 对关键字进行重载
操作符是函数的简计法,操作符其实就是函数
(.
、()
、^
和 ..
运算符无法进行重载。 []
运算符也被视为非可重载运算符。 使用索引器以支持对用户定义的类型编制索引。)
operator 关键字的主要作用是用来重载运算符的,还可以用于类或结构中类型的自定义转换。
下面看个例子
x.y 成员访问运算符和表达式 .
?.
和?[\]
(null 条件运算符):仅当操作数为非 null 时才用于执行成员或元素访问运算()
(调用):用于调用被访问的方法或调用委托^
(从末尾开始索引):指示元素位置来自序列的末尾..
(范围):指定可用于获取一系列序列元素的索引范围(这里运用,0才是最后一位,这就很坑)
Null 条件运算符 ?. 和 ?[]
Null 条件运算符在 C# 6 及更高版本中可用,仅当操作数的计算结果为非 null 时,null 条件运算符才会将成员访问 ?.
或元素访问 ?[]
运算应用于其操作数;否则,将返回 null
。 即:
- 如果
a
的计算结果为null
,则a?.x
或a?[x]
的结果为null
。 - 如果
a
的计算结果为非 null,则a?.x
或a?[x]
的结果将分别与a.x
或a[x]
的结果相同。
checked
checked
关键字用于对整型类型算术运算和转换显式启用溢出检查。(显式即明显的,明确的 ,var是不显式,由编译器推断类型)
默认情况下,如果表达式仅包含常量值,且产生的值在目标类型范围之外,则会导致编译器错误。 如果表达式包含一个或多个非常量值,则编译器不检测溢出。
也就是说,int 溢出了,溢出会同余定理,不会报错,使用checked可以检测这些,抛出溢出异常,配合unchecked可以在checked范围中高出unchecked
new
匿名类型的实例化
要创建匿名类型的实例,请使用 new
运算符和对象初始值设定项语法:
var example = new { Greeting = "Hello", Name = "World" };
Console.WriteLine($"{example.Greeting}, {example.Name}!");
// Output:
// Hello, World!
重写与new
什么条件下才能对父类方法进行重写:父类中方法必须被virtual关键字修饰的方法才能被子类重写,如何父类方法没有使用virtual关键字,在子类中重写父类方法时就会编译错误;在子类中对父类进行重写时,需要使用override关键字作为标识;例如B类继承A类,并重写A类中Print方法时,没有使用override关键字的的话会出现警告,告诉我们这不是重写,这是隐式的new。
class A
{
public A()
{
Print();
}
public virtual void Print()
{
Console.WriteLine("我是A类");
}
}
class B : A
{
public B()
{
Print();
}
public override void Print()
{
Console.WriteLine("我是B类");
}
}
如果使用new关键字对print方法进行修饰,B类中Print方法则不是对A类中Print方法进行重写,关于这一点我会在下面进行解读。
如果父类方法被子类重写之后,通过创建子类对象指向父类时,使用Print方法时,调用的是父类还是子类的方法?
A a = new B();
a.Print();
运行后的结果为:
在创建B类的实例时,首先会运行类中构造函数,但是会首先执行父类中的构造函数,当执行父类中构造函数中Print方法时,首先判断Print方法是否已经被重写,如果该方法被重写的话,则会执行子类中重写的方法;执行完成父类的构造函数之后,就会执行子类中构造函数,子类构造函数调用Print函数时,首先会判断子类中是否含有Print方法,有的话则会调用自身的Print方法,否则调用父类中Print方法;最后a.Print()也是首先判断A类中Print方法是否被重写,如果被重写的话则调用子类中Print方法。
因此得出的结论是:当父类方法被子类重写之后,只要创建子类的实例,当调用被重写的方法时,最终运行的都是子类中的方法。
如果子类中的Print方法被new关键字而不是override关键字修饰时,父类中Print方法是否被重写?
答:没有被重写。被new 关键字修饰后,Print方法不是重写方法,而是B类中的新方法。
class A
{
public A()
{
Print();
}
public virtual void Print()
{
Console.WriteLine("我是A类");
}
}
class B : A
{
public B()
{
Print();
}
public new void Print()
{
Console.WriteLine("我是B类");
}
}
当将override改为new关键字之后,再次运行得到以下的结果:
这说明了当使用了new关键字之后,只要跟父类有关,运行的Print方法都是运行父类中Print方法。
取反~
获得把每位值取反,如果,整数变负数,负数绝对值+1,负数变整数,绝对值+1
指针相关运算符
可以使用以下运算符来使用指针:
- 一元
&
(address-of) 运算符:用于获取变量的地址 - 一元
*
(指针间接)运算符:用于获取指针指向的变量 ->
(成员访问)和[\]
(元素访问)运算符- 算术运算符
+
、-
、++
和--
- 比较运算符
==
、!=
、<
、>
、<=
和>=
有关指针类型的信息,请参阅指针类型。
任何带指针的运算都需要使用 unsafe 上下文。 必须使用 -unsafe
编译器选项编译包含不安全块的代码。
x * y 乘法
整数乘法的时候如果int.MaxValue2 ,那么调用的是int operator * (返回类型位方法),就会导致返回值溢出,被checked出异常,但是如果有一个是int.MaxValue2L,int.MaxValue就会被隐式转换位long,然后用long operator *(返回类型位方法)
无穷大乘*会返回NaN(not a number)
x / y除法
整数除法
对于整数类型的操作数,/
运算符的结果为整数类型,并且等于两个操作数之商向零舍入后的结果:
Console.WriteLine(13 / 5); // output: 2
Console.WriteLine(-13 / 5); // output: -2
Console.WriteLine(13 / -5); // output: -2
Console.WriteLine(-13 / -5); // output: 2
若要获取浮点数形式的两个操作数之商,请使用 float
、double
或 decimal
类型:
Console.WriteLine(13 / 5.0); // output: 2.6
int a = 13;
int b = 5;
Console.WriteLine((double)a / b); // output: 2.6
浮点除法
浮点没有除0异常,符号位(如果0是整数,那么0符号位无论如何都是正)正负决定了结果是正无穷(double.PositiveInfinity)还是负无穷(double.NegativeInfinity)
对于 float
、double
和 decimal
类型,/
运算符的结果为两个操作数之商:
Console.WriteLine(16.8f / 4.1f); // output: 4.097561
Console.WriteLine(16.8d / 4.1d); // output: 4.09756097560976
Console.WriteLine(16.8m / 4.1m); // output: 4.0975609756097560975609756098
如果操作数之一为 decimal
,那么另一个操作数不得为 float
和 double
,因为 float
和 double
都无法隐式转换为 decimal
。 必须将 float
或 double
操作数显式转换为 decimal
类型。 如需详细了解数值类型之间的转换,请参阅内置数值转换。
x % y 求余
余数运算符 %
计算左侧操作数除以右侧操作数后的余数。
整数余数
对于整数类型的操作数,a % b
的结果是 a - (a / b) * b
得出的值。 非零余数的符号与左侧操作数的符号相同,如下例所示:
Console.WriteLine(5 % 4); // output: 1
Console.WriteLine(5 % -4); // output: 1
Console.WriteLine(-5 % 4); // output: -1
Console.WriteLine(-5 % -4); // output: -1
使用 Math.DivRem 方法计算整数除法和余数结果。
浮点余数
对于 float
和 double
操作数,有限的 x
和 y
的 x % y
的结果是值 z
,因此
z
(如果不为零)的符号与x
的符号相同。z
的绝对值是|x| - n * |y|
得出的值,其中n
是小于或等于|x| / |y|
的最大可能整数,|x|
和|y|
分别是x
和y
的绝对值。
计算余数的此方法类似于用于整数操作数的方法,但与 IEEE 754 规范不同。 如果需要符合 IEEE 754 规范的余数运算,请使用 Math.IEEERemainder 方法。
有关非限定操作数的 %
运算符行为的信息,请参阅 C# 语言规范的余数运算符章节。
对于 decimal
操作数,余数运算符 %
等效于 System.Decimal 类型的余数运算符。
以下示例演示了具有浮动操作数的余数运算符的行为:
Console.WriteLine(-5.2f % 2.0f); // output: -1.2
Console.WriteLine(5.9 % 3.1); // output: 2.8
Console.WriteLine(5.9m % 3.1m); // output: 2.8
+加法与-减法
正无穷+/-负无穷=NaN
x << y 左位移与 x >> y右位移
很十进制一样,左移就是乘10,右移是除10,计算机二进制也是这样,前提是不溢出,右移是容易溢出的,(左移最低位补0和右翼根据符号位补)
uint x = 0b_1100_1001_0000_0000_0000_0000_0001_0001;
Console.WriteLine($"Before: {Convert.ToString(x, toBase: 2)}");
uint y = x << 4;
Console.WriteLine($"After: {Convert.ToString(y, toBase: 2)}");
// Output:
// Before: 11001001000000000000000000010001
// After: 10010000000000000000000100010000
is 判断类型是否兼容
如果 E
的结果为非 null 且可以通过引用转换、装箱转换或取消装箱转换来转换为类型 T
,则 E is T
表达式将返回 true
;否则,它将返回 false
。 is
运算符不会考虑用户定义的转换。
public class Base { }
public class Derived : Base { }
public static class IsOperatorExample
{
public static void Main()
{
object b = new Base();
Console.WriteLine(b is Base); // output: True
Console.WriteLine(b is Derived); // output: False
object d = new Derived();
Console.WriteLine(d is Base); // output: True
Console.WriteLine(d is Derived); // output: True
}
}
int i = 27;
Console.WriteLine(i is System.IFormattable); // output: True
object iBoxed = i;
Console.WriteLine(iBoxed is int); // output: True
Console.WriteLine(iBoxed is long); // output: False
int i = 23;
object iBoxed = i;
int? jNullable = 7;
if (iBoxed is int a && jNullable is int b)
{
Console.WriteLine(a + b); // output 30
}
as
相当于下面的作用
E is T ? (T)(E) : (T)null
&与&&(|与||类似就不再搞了)
&
运算符计算操作数的逻辑与。 如果 x
和 y
的计算结果都为 true
,则 x & y
的结果为 true
。 否则,结果为 false
。
即使左侧操作数计算结果为 false
,&
运算符也会计算这两个操作数,而在这种情况下,无论右侧操作数的值为何,运算结果都为 false
。
条件逻辑 AND 运算符 &&
也计算操作数的逻辑 AND,但如果左侧操作数的计算结果为 false
,它就不会计算右侧操作数。
^异或运算符
Console.WriteLine(true ^ true); // output: False
Console.WriteLine(true ^ false); // output: True
Console.WriteLine(false ^ true); // output: True
Console.WriteLine(false ^ false); // output: False
&位与
uint a = 0b_1111_1000;
uint b = 0b_1001_1101;
uint c = a & b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10011000
|位或
uint a = 0b_1010_0000;
uint b = 0b_1001_0001;
uint c = a | b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 10110001
^位异或
uint a = 0b_1111_1000;
uint b = 0b_0001_1100;
uint c = a ^ b;
Console.WriteLine(Convert.ToString(c, toBase: 2));
// Output:
// 11100100
typeof
确认类型的
?
让a可以赋值为null(如果不赋值默认值0),而且提供HasValue属性
int? a = null;
Console.WriteLine(a.HasValue);
Console.WriteLine(a.GetType().Name);
default
确认默认值的,有点麻烦,而在default(枚举类型时)默认选下标为0的,如果你建立的枚举中没有等于0的,显示0
sizeof
计算占用字节大小的,关系到unsafe,比较麻烦,以后厉害点再说吧
因为操作内存,使用不安全的