运算符
C#支持下表中的运算符和表达式,在下表中,按优先级最高到最低的顺序列出了运算符。
类别
|
运算符
|
主要运算符
|
x.y x?.y f(x) a[x] x++ x-- x! x->y new typeof default checked unchecked delegate nameof sizeof stackalloc
|
一元运算符
|
+x -x !x ~x ++x --x ^x (T)x await &x *x 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
|
类型测试运算符
|
is as
|
相等性运算符
|
x==y x!=y
|
逻辑运算符
|
x&y x^y x|y
|
条件逻辑运算符
|
x&&y x||y
|
空合并运算符
|
x??y
|
条件运算符
|
c?t:f
|
赋值运算符
|
x=y x+=y x-=y x*=y x/=y x%=y x&=y x|=y x^=y x<<=y x>>=y x??=y
|
lambda表达式
|
=>
|
注意:有4个运算符(sizeof、*、->和&)只能用于不安全的代码。
复合赋值运算符
复合赋值运算符是使用赋值运算符和另外一个运算符的一种简写形式。例如,不必编写x=x+2,而是可以使用复合赋值运算符x+=2。
递增1的运算使用得更加频繁,所以有其自己的简写形式x++:
int x=1;
int x+=2;
x++;
其它所有复合赋值运算符都可以使用简写方式。C#8引入了一种新的复合赋值运算符:空合并复合赋值运算符。
你可能想知道为什么++递增运算符有两种形式。把运算符放在表达式的前面称为前置,把运算符放在表达式的后面称为后置。要点是注意它们的行为方式有所不同。
递增或递减运算符可以用作整个表达式,也可以用于表达式的内部。当单独使用时,前缀和后缓版本的作用是相同的,都对应于语句x=x+1。但当它们用于较长的表达式内部时,把运算符放在前面(++x)会在计算表达式之前递增x,换言之,递增了x后,在表达式中使用新值进行计算。而把运算符放在后面(x++)会在计算表达式之后递增x,即使用x的原始值计算表达式。下面的例子使用++递增运算符说明了它们的区别:
void PrefixAndPostfix( )
{
int x=5;
if (++x == 6) // true - x is incremented to 6 before the evaluation
{
Console.WriteLine("This will execute");
if (x++ == 6) // true - x is incremented to 7 after the evaluation
{
Console.WriteLine("The value of x is: (x)"); // x has the value 7
}
}
}
下面介绍在C#代码中频繁使用的运算符和新增的运算符。
条件运算符
条件运算符(?:)也称为三元运算符,是if...else结构的简化形式。其名称的出处是它带有3个操作数。它首先判断一个条件,如果条件为真,就返回一个值;如果条件为假,则返回另一个值。其语法如下:
condition ? true_value: false_ value
其中condition 是要判断的布尔表达式,truc_value 是condition 为真时返回的值,false_value是condition为假时返回的值。
恰当地使用条件运算符,可以使程序更加简洁。它特别适合于给调用的函数提供两个参数中的一个。使用它可以把布尔值快速转换为字符串值true或false。它也很适合于显示一个单词的正确的单数形式或复数形式:
int x= 1;
string s=x+" ";
s +=(x == 1.?"man":"men");
Console.WriteLine(s);
如果×等于1,这段代码就显示1man;如果x等于其他数,就显示其正确的复数形式。但要注意,如果结果需要本地化为不同的语言,就必须编写更复杂的例程,以考虑到不同语言的不同语法规则。
checked 和unchecked运算符
考虑下面的代码:
byte b = byte.MaxValue;
b++;
Console.WriteLine(b);
byte 数据类型只能包含0~255 的数。将byte.MaxValue 赋值给一个byte变量,得到255。对于255,字节中所有可用的8个位都得到设置:11111111。所以递增这个值会导致溢出,并得到0。
为了在这种情况下生成异常,C#提供了checked和 unchecked运算符。如果把一个代码块标记为checked, CLR 就会执行溢出检查,如果发生溢出,就抛出OverflowException 异常。下面修改上述代码,使之包含checked运算符:
byte b= 255;
checked
{
b++;
}
Console.WriteLine(b);
除了编写一个checked块,也可以在表达式中使用checked关键字:
b= checked(b+ 3);
运行这段代码会抛出OverflowException。
通过在csproj 文件中添加 CheckForOverflowUnderflow设置,可以对所有未标记的代码进行溢出检查:
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
<CheckForOverflowUnderflow>true</CheckForOverflowUnderflow>
</PropertyGroup>
在配置项目设置来进行溢出检查时,可以使用unchecked运算符来标记不应该进行溢出检查的代码。
注意:
默认不检查上溢出和下溢出,因为执行这种检查会影响性能。使用checked作为项目的默认设置时,每一个算术运算的结果都需要验证其值是否越界。i++是for循环中大量使用的一种算术运算。为了避免这种性能影响,最好一直保留默认设置(Check for Arithmetic Overflow/Underflow),在需要时使用checked运算符。
is 和 as 运算符
is 和as 运算符可以检查对象是否与特定的类型兼容,在类层次结构中很有用。
假设有一个简单的类层次结构。类DerivedClass 派生自类BaseClass.你可以把DerivedClass类的变量赋值给BascClass 类型的变量,BaseClass的所有成员在DerivedClass中都是可用的。在下面然示例中,发生了隐式转换:
Baseclass = new();
Derivedclass= new();
baseclass = derivedClass;
如果有一个BaseClass类型的参数,想将其赋值给DerivedClass类型的变量,这是不能通过隐转换实现的。对于SomeAction( )方法来说,可以传入BaseClass或者派生自BaseClass的任何类型的例,但这不一定会成功。在这里,可以使用as 运算符。as运算符要么返回一个DerivedClass实例 ( 如果变量是该类型 ),要么返回null:
public void SomeAction (BaseClass baseclass)
{
Derivedclass? derivedclass=baseClass
as Derivedclass
if (derivedclass != nu11)
{
// use the derivedclass variable
}
}
除了使用as运算符,还可以使用 is 运算符。如果转换成功,is将返回true,否则返回false。使用is运算符时,可以指定一个变量,如果is运算符返回true,就为该变量赋值:
public void SomeAction(Baseclass baseclass)
{
if (baseClass
is Derivedclass derivedclass)
{
// use the derivedclass variable
}
}