MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

C#中的运算符与表达式解析

2024-10-074.4k 阅读

一、C# 运算符概述

C# 是一种功能强大的编程语言,其运算符体系丰富且灵活,为开发者提供了高效处理各种数据操作的能力。运算符是一种告诉编译器执行特定数学、逻辑或其他操作的符号。表达式则是由运算符和操作数组成的式子,它会根据运算符的规则计算出一个结果。

在 C# 中,运算符按照其功能可以分为多个类别,常见的有算术运算符、赋值运算符、比较运算符、逻辑运算符、位运算符、条件运算符等。不同类型的运算符在程序中承担着不同的角色,下面我们将详细介绍各类运算符及其相关表达式。

二、算术运算符与表达式

(一)基本算术运算符

  1. 加法运算符(+) 加法运算符用于将两个数值相加。操作数可以是整数、浮点数等数值类型。例如:
int num1 = 5;
int num2 = 3;
int result = num1 + num2;
Console.WriteLine(result);  // 输出 8

当操作数为浮点数时,同样适用:

double d1 = 2.5;
double d2 = 1.5;
double dResult = d1 + d2;
Console.WriteLine(dResult);  // 输出 4

需要注意的是,C# 中的字符串类型也支持加法运算符,不过此时它的作用是字符串连接。

string str1 = "Hello";
string str2 = " World";
string strResult = str1 + str2;
Console.WriteLine(strResult);  // 输出 Hello World
  1. 减法运算符(-) 减法运算符用于计算两个数值之间的差值。示例如下:
int a = 10;
int b = 4;
int subResult = a - b;
Console.WriteLine(subResult);  // 输出 6
  1. 乘法运算符(*) 乘法运算符用于将两个数值相乘。
int x = 5;
int y = 6;
int mulResult = x * y;
Console.WriteLine(mulResult);  // 输出 30
  1. 除法运算符(/) 除法运算符用于计算两个数值的商。对于整数类型的操作数,结果将是整数,小数部分会被截断。
int m = 10;
int n = 3;
int divResult = m / n;
Console.WriteLine(divResult);  // 输出 3

如果至少有一个操作数是浮点数,结果将是浮点数类型。

double num3 = 10.0;
int num4 = 3;
double divDoubleResult = num3 / num4;
Console.WriteLine(divDoubleResult);  // 输出 3.3333333333333335
  1. 取模运算符(%) 取模运算符用于计算两个整数相除的余数。
int p = 10;
int q = 3;
int modResult = p % q;
Console.WriteLine(modResult);  // 输出 1

(二)自增和自减运算符

  1. 自增运算符(++) 自增运算符有两种形式:前置自增(++变量名)和后置自增(变量名++)。前置自增会先将变量的值加 1,然后返回加 1 后的值;后置自增会先返回变量原来的值,然后再将变量的值加 1。
int num5 = 5;
int preIncrement = ++num5;
Console.WriteLine(preIncrement);  // 输出 6,此时 num5 的值也为 6

int num6 = 5;
int postIncrement = num6++;
Console.WriteLine(postIncrement);  // 输出 5,此时 num6 的值为 6
  1. 自减运算符(--) 自减运算符同样有前置自减(--变量名)和后置自减(变量名--)两种形式,其原理与自增运算符类似,只是将变量的值减 1。
int num7 = 5;
int preDecrement = --num7;
Console.WriteLine(preDecrement);  // 输出 4,此时 num7 的值也为 4

int num8 = 5;
int postDecrement = num8--;
Console.WriteLine(postDecrement);  // 输出 5,此时 num8 的值为 4

三、赋值运算符与表达式

(一)简单赋值运算符(=)

简单赋值运算符用于将右侧表达式的值赋给左侧的变量。

int num9 = 10;
string str3 = "Hello";

(二)复合赋值运算符

  1. 加法赋值运算符(+=) 加法赋值运算符将变量的值与右侧表达式的值相加,并将结果赋给该变量。
int num10 = 5;
num10 += 3;
Console.WriteLine(num10);  // 输出 8,相当于 num10 = num10 + 3;
  1. 减法赋值运算符(-=) 减法赋值运算符将变量的值减去右侧表达式的值,并将结果赋给该变量。
int num11 = 10;
num11 -= 4;
Console.WriteLine(num11);  // 输出 6,相当于 num11 = num11 - 4;
  1. 乘法赋值运算符(*=) 乘法赋值运算符将变量的值与右侧表达式的值相乘,并将结果赋给该变量。
int num12 = 5;
num12 *= 3;
Console.WriteLine(num12);  // 输出 15,相当于 num12 = num12 * 3;
  1. 除法赋值运算符(/=) 除法赋值运算符将变量的值除以右侧表达式的值,并将结果赋给该变量。
int num13 = 10;
num13 /= 2;
Console.WriteLine(num13);  // 输出 5,相当于 num13 = num13 / 2;
  1. 取模赋值运算符(%=) 取模赋值运算符将变量的值与右侧表达式的值进行取模运算,并将结果赋给该变量。
int num14 = 10;
num14 %= 3;
Console.WriteLine(num14);  // 输出 1,相当于 num14 = num14 % 3;

四、比较运算符与表达式

(一)相等运算符(==)

相等运算符用于判断两个操作数是否相等,返回一个布尔值(true 或 false)。

int num15 = 5;
int num16 = 5;
bool isEqual = num15 == num16;
Console.WriteLine(isEqual);  // 输出 true

(二)不等运算符(!=)

不等运算符用于判断两个操作数是否不相等,同样返回布尔值。

int num17 = 5;
int num18 = 3;
bool isNotEqual = num17 != num18;
Console.WriteLine(isNotEqual);  // 输出 true

(三)大于运算符(>)

大于运算符用于判断左侧操作数是否大于右侧操作数。

int num19 = 10;
int num20 = 5;
bool isGreater = num19 > num20;
Console.WriteLine(isGreater);  // 输出 true

(四)小于运算符(<)

小于运算符用于判断左侧操作数是否小于右侧操作数。

int num21 = 3;
int num22 = 5;
bool isLess = num21 < num22;
Console.WriteLine(isLess);  // 输出 true

(五)大于等于运算符(>=)

大于等于运算符用于判断左侧操作数是否大于或等于右侧操作数。

int num23 = 5;
int num24 = 5;
bool isGreaterOrEqual = num23 >= num24;
Console.WriteLine(isGreaterOrEqual);  // 输出 true

(六)小于等于运算符(<=)

小于等于运算符用于判断左侧操作数是否小于或等于右侧操作数。

int num25 = 3;
int num26 = 5;
bool isLessOrEqual = num25 <= num26;
Console.WriteLine(isLessOrEqual);  // 输出 true

五、逻辑运算符与表达式

(一)逻辑与运算符(&&)

逻辑与运算符用于连接两个布尔表达式,只有当两个表达式都为 true 时,整个表达式才为 true。如果第一个表达式为 false,第二个表达式将不会被计算,这称为短路求值。

bool b1 = true;
bool b2 = false;
bool result1 = b1 && b2;
Console.WriteLine(result1);  // 输出 false

int num27 = 5;
bool result2 = num27 > 3 && num27 < 10;
Console.WriteLine(result2);  // 输出 true

(二)逻辑或运算符(||)

逻辑或运算符连接两个布尔表达式,只要其中一个表达式为 true,整个表达式就为 true。同样存在短路求值,当第一个表达式为 true 时,第二个表达式不会被计算。

bool b3 = true;
bool b4 = false;
bool result3 = b3 || b4;
Console.WriteLine(result3);  // 输出 true

int num28 = 5;
bool result4 = num28 < 3 || num28 > 0;
Console.WriteLine(result4);  // 输出 true

(三)逻辑非运算符(!)

逻辑非运算符用于对一个布尔表达式取反,即如果表达式为 true,则取反后为 false;如果表达式为 false,则取反后为 true。

bool b5 = true;
bool result5 =!b5;
Console.WriteLine(result5);  // 输出 false

int num29 = 5;
bool result6 =!(num29 > 3);
Console.WriteLine(result6);  // 输出 false

六、位运算符与表达式

(一)按位与运算符(&)

按位与运算符对两个整数的每一位进行与操作,只有当对应的位都为 1 时,结果位才为 1,否则为 0。

int num30 = 5;  // 二进制表示为 00000101
int num31 = 3;  // 二进制表示为 00000011
int bitwiseAndResult = num30 & num31;
Console.WriteLine(bitwiseAndResult);  // 输出 1,二进制表示为 00000001

(二)按位或运算符(|)

按位或运算符对两个整数的每一位进行或操作,只要对应的位有一个为 1,结果位就为 1,只有当对应的位都为 0 时,结果位才为 0。

int num32 = 5;  // 二进制表示为 00000101
int num33 = 3;  // 二进制表示为 00000011
int bitwiseOrResult = num32 | num33;
Console.WriteLine(bitwiseOrResult);  // 输出 7,二进制表示为 00000111

(三)按位异或运算符(^)

按位异或运算符对两个整数的每一位进行异或操作,当对应的位不同时,结果位为 1,当对应的位相同时,结果位为 0。

int num34 = 5;  // 二进制表示为 00000101
int num35 = 3;  // 二进制表示为 00000011
int bitwiseXorResult = num34 ^ num35;
Console.WriteLine(bitwiseXorResult);  // 输出 6,二进制表示为 00000110

(四)按位取反运算符(~)

按位取反运算符对一个整数的每一位进行取反操作,即将 0 变为 1,1 变为 0。

int num36 = 5;  // 二进制表示为 00000101
int bitwiseNotResult = ~num36;
Console.WriteLine(bitwiseNotResult);  // 输出 -6,因为按位取反后是 11111010,在有符号整数中表示 -6

(五)左移运算符(<<)

左移运算符将一个整数的二进制表示向左移动指定的位数,右侧空出的位用 0 填充。

int num37 = 5;  // 二进制表示为 00000101
int leftShiftResult = num37 << 2;
Console.WriteLine(leftShiftResult);  // 输出 20,二进制表示为 00010100,相当于 5 * 2^2

(六)右移运算符(>>)

右移运算符将一个整数的二进制表示向右移动指定的位数,对于有符号整数,左侧空出的位用符号位填充(正数用 0 填充,负数用 1 填充);对于无符号整数,左侧空出的位用 0 填充。

int num38 = 5;  // 二进制表示为 00000101
int rightShiftResult = num38 >> 2;
Console.WriteLine(rightShiftResult);  // 输出 1,二进制表示为 00000001,相当于 5 / 2^2 向下取整

七、条件运算符与表达式

条件运算符(?:)也称为三元运算符,它是 C# 中唯一的一个三元运算符。其语法为:条件? 表达式 1 : 表达式 2。如果条件为 true,则返回表达式 1 的值;如果条件为 false,则返回表达式 2 的值。

int num39 = 5;
string result7 = num39 > 3? "大于 3" : "小于等于 3";
Console.WriteLine(result7);  // 输出 大于 3

八、运算符优先级与结合性

(一)运算符优先级

在一个复杂的表达式中,不同运算符的执行顺序是由运算符优先级决定的。优先级高的运算符先执行,优先级低的运算符后执行。例如,乘法和除法的优先级高于加法和减法。以下是 C# 中常见运算符的大致优先级顺序(从高到低):

  1. 括号(()):括号内的表达式先计算,通过括号可以改变运算的优先级顺序。
  2. 单目运算符:如 ++、--、!、~ 等。
  3. 算术运算符:先乘除(*、/、%),后加减(+、-)。
  4. 移位运算符:<<、>>。
  5. 关系运算符:>、<、>=、<=、==、!=。
  6. 逻辑运算符:先按位与(&),再按位异或(^),然后按位或(|),接着逻辑与(&&),最后逻辑或(||)。
  7. 条件运算符(?:)
  8. 赋值运算符:=、+=、-= 等。

(二)运算符结合性

当一个表达式中出现多个优先级相同的运算符时,运算符的结合性决定了运算的顺序。C# 中大部分运算符具有左结合性,即从左到右依次计算;而赋值运算符和条件运算符具有右结合性,即从右到左依次计算。例如:

int num40 = 5 + 3 - 2;  // 先计算 5 + 3,再计算 8 - 2,结果为 6,体现左结合性

int num41;
num41 = num40 = 10;  // 先将 10 赋给 num40,再将 num40 的值赋给 num41,体现右结合性

九、运算符重载

在 C# 中,除了预定义的运算符行为,开发者还可以为自定义类型重载运算符,使得自定义类型能够像基本数据类型一样使用运算符。例如,假设有一个表示二维向量的结构体 Vector2

struct Vector2
{
    public float x;
    public float y;

    public Vector2(float x, float y)
    {
        this.x = x;
        this.y = y;
    }

    // 重载加法运算符
    public static Vector2 operator +(Vector2 a, Vector2 b)
    {
        return new Vector2(a.x + b.x, a.y + b.y);
    }
}

class Program
{
    static void Main()
    {
        Vector2 v1 = new Vector2(1, 2);
        Vector2 v2 = new Vector2(3, 4);
        Vector2 result8 = v1 + v2;
        Console.WriteLine($"({result8.x}, {result8.y})");  // 输出 (4, 6)
    }
}

通过运算符重载,可以为自定义类型提供更加直观和方便的操作方式,增强代码的可读性和易用性。

十、表达式求值与类型转换

在表达式求值过程中,C# 会根据运算符的规则和操作数的类型进行计算。当操作数类型不同时,可能会发生类型转换。

  1. 隐式类型转换:当一个较小类型的操作数与一个较大类型的操作数进行运算时,较小类型的操作数会自动隐式转换为较大类型。例如,当一个 int 类型与一个 double 类型进行运算时,int 类型会隐式转换为 double 类型。
int num42 = 5;
double num43 = 3.5;
double result9 = num42 + num43;
Console.WriteLine(result9);  // 输出 8.5
  1. 显式类型转换:当需要将一个较大类型的操作数转换为较小类型时,需要进行显式类型转换,也称为强制类型转换。例如,将一个 double 类型转换为 int 类型时,小数部分会被截断。
double num44 = 5.6;
int num45 = (int)num44;
Console.WriteLine(num45);  // 输出 5

在复杂表达式中,类型转换和运算符优先级、结合性等因素相互作用,共同决定了表达式的最终求值结果。开发者需要清楚地了解这些规则,以确保程序的正确性和预期的行为。

通过对 C# 中各种运算符与表达式的深入解析,我们可以更加灵活和高效地编写 C# 程序,实现各种复杂的逻辑和数据处理任务。在实际编程中,要根据具体需求合理选择和使用运算符,同时注意运算符的优先级、结合性以及类型转换等问题,避免出现逻辑错误和意外的结果。