C# 运算符知识大赏

C# 运算符是用于执行各种操作的符号,例如算术运算、比较运算、逻辑运算等。它们是构建 C# 表达式的基础。以下是 C# 中常用的运算符类型:

算术运算符

C# 中的算术运算符用于执行基本的数学运算
下表显示了 C# 支持的所有算术运算符。假设变量 A 的值为 10,变量 B 的值为 20

运算符描述实例
+把两个操作数相加A + B 将得到 30
-从第一个操作数中减去第二个操作数A - B 将得到 -10
*把两个操作数相乘A * B 将得到 200
/分子除以分母B / A 将得到 2
%取模运算符,整除后的余数B % A 将得到 0
++自增运算符,整数值增加 1A++ 将得到 11
自减运算符,整数值减少 1A-- 将得到 9

1. 加法运算符 ( + )

用途

  • 用于将两个数值相加。

  • 也用于字符串连接。

int a = 5;
int b = 3;
int sum = a + b; // sum 的值为 8


string str1 = "Hello";
string str2 = "World";
string message = str1 + " " + str2; // message 的值为 "Hello World"


double a = 3.14;
double b = 2.71;
double sum = a + b; // sum 的值为 5.85


float x = 1.5f;
float y = 2.5f;
float result = x + y; // result 的值为 4.0


特殊值处理:在处理浮点数运算的结果时,要注意 NaN 和 Infinity 等特殊值。
可以使用 double.IsNaN()double.IsInfinity() 等方法来检查这些特殊值。

注意事项

  • 当操作数中有一个是字符串时,+ 运算符执行字符串连接。

  • 对于浮点数,+ 运算符执行浮点数加法。


2. 减法运算符 ( - )

用途

  • 用于将两个数值相减。
int a = 10;
int b = 4;
int difference = a - b; // difference 的值为 6

注意事项

  • 对于浮点数,- 运算符执行浮点数减法。

3. 乘法运算符 ( * )

用途

  • 用于将两个数值相乘。
int a = 6;
int b = 7;
int product = a * b; // product 的值为 42

注意事项

  • 对于浮点数,* 运算符执行浮点数乘法。

4. 除法运算符 ( / )

用于将一个数值(被除数)除以另一个数值(除数)
用途

  • 用于将两个数值相除。
int a = 15;
int b = 3;
int quotient = a / b; // quotient 的值为 5


double x = 10.0;
double y = 3.0;
double result = x / y; // result 的值为 3.333...

注意事项

  • 如果两个操作数都是整数,则结果也是整数(截断小数部分)。

  • 如果其中一个操作数是浮点数,则结果是浮点数。

  • 除数不能为0,否则会引发 DivideByZeroException 异常


5. 取模运算符 ( % )

取模运算符 % 用于计算两个整数相除的余数。它的操作数必须是整数。

被除数 % 除数

用途

  • 用于计算两个数值相除的余数。

  • 判断奇偶

    • 偶数的定义: 偶数是可以被 2 整除的整数,余数为 0。

    • 奇数的定义: 奇数是被 2 除,余数为 1 的整数。

int a = 17;
int b = 5;
int remainder = a % b; // remainder 的值为 2


int a = 10;
int b = 3;
int remainder1 = a % b; // remainder1 的值为 1 (10 除以 3 的余数为 1)


int c = 25;
int d = 7;
int remainder2 = c % d; // remainder2 的值为 4 (25 除以 7 的余数为 4)
// 负数取模
int e = -10;
int f = 3;
int remainder3 = e % f; // remainder3 的值为 -1 (-10 除以 3 的余数为 -1)


int g = 10;
int h = -3;
int remainder4 = g % h; // remainder4 的值为 1 (10 除以 -3 的余数为 1)


int i = -10;
int j = -3;
int remainder5 = i % j; // remainder5 的值为 -1 (-10 除以 -3 的余数为 -1)
int k = 1;
int l = 5;
int remainder6 = k % l; // remainder6 的值为 1 (1 除以 5 的余数为 1)
int m = 10;
int n = 0;
// int remainder7 = m % n; // 这会引发 DivideByZeroException 异常
// 判断奇偶
int number1 = 10;
int number2 = 7;


if (number1 % 2 == 0)
{
    Console.WriteLine($"{number1} 是偶数"); // 输出:10 是偶数
}
else
{
    Console.WriteLine($"{number1} 是奇数");
}


if (number2 % 2 == 0)
{
    Console.WriteLine($"{number2} 是偶数");
}
else
{
    Console.WriteLine($"{number2} 是奇数"); // 输出:7 是奇数
}


注意事项

  • 取模运算符只能用于整数。

  • 被除数不能为0,否则会引发 DivideByZeroException 异常

  • 在 C# 中,取模运算的结果的符号与被除数的符号相同

  • 当被除数小于除数时,余数就是被除数本身。


  1. 递增运算符 (++)
  • 定义

    • ++ 运算符将变量的值增加 1。

    • 它可以作为**前缀运算符****(++变量****)后缀运算符****(变量++)**使用。

  • 前缀递增:++变量:先将变量的值增加 1,然后返回增加后的值。

int x = 5;
int y = ++x; // x 现在是 6,y 现在也是 6
  • 后缀递增:变量++:先返回变量的原始值,然后将变量的值增加 1。
int a = 5;
int b = a++; // a 现在是 6,b 现在是 5

---
  1. 递减运算符 ( – )
  • 定义:

    • – 运算符将变量的值减少 1。

    • 它可以作为前缀运算符– 变量)或**后缀运算符****(变量–)**使用。

  • 前缀递减:--变量:先将变量的值减少 1,然后返回减少后的值。

int m = 5;
int n = --m; // m 现在是 4,n 现在也是 4
  • 后缀递减:变量--:先返回变量的原始值,然后将变量的值减少 1。
int p = 5;
int q = p--; // p 现在是 4,q 现在是 5

示例和解释

int i = 0;
Console.WriteLine(i++); // 输出 0,然后 i 变为 1
Console.WriteLine(++i); // i 变为 2,然后输出 2


int j = 5;
int result1 = j++ + 10; // result1 为 15,j 变为 6
int result2 = ++j + 10; // j 变为 7,result2 为 17


Console.WriteLine($"i: {i}, j: {j}, result1: {result1}, result2: {result2}");


输出:
0
2
i: 2,
j: 7,
result1: 15,
result2: 17

注意事项

  • ++-- 运算符只能用于变量,不能用于常量或表达式。

  • 在使用这些运算符时,要特别注意前缀和后缀形式的区别,因为它们在表达式中的行为不同。

  • 使用时候要注意代码的可读性,如果过于复杂的嵌套,会使代码难以阅读和维护。

c = a++: 先将 a 赋值给 c,再对 a 进行自增运算。
c = ++a: 先将 a 进行自增运算,再将 a 赋值给 c 。
c = a–: 先将 a 赋值给 c,再对 a 进行自减运算。
c = --a: 先将 a 进行自减运算,再将 a 赋值给 c


运算符优先级

  • 乘法、除法和取模运算符的优先级高于加法和减法运算符。

  • 可以使用括号 () 来改变运算顺序。

int result1 = 10 + 5 * 2; // result1 的值为 20 (先算乘法)
int result2 = (10 + 5) * 2; // result2 的值为 30 (先算括号里的加法)

---

类型转换

  • 在进行算术运算时,如果操作数的类型不同,C# 会自动进行类型转换。

  • 通常情况下,较小的类型会自动转换为较大的类型。

  • 例如,int 会自动转换为 double

int a = 10;
double b = 3.5;
double result = a + b; // a 会自动转换为 double,result 的值为 13.5

---

Math.DivRem

  • Math.DivRem 方法用于同时计算整数除法的商和余数。

  • 它比单独使用 /% 运算符更高效。

int a = 17;
int b = 5;
int quotient;
int remainder;
Math.DivRem(a, b, out quotient, out remainder);
Console.WriteLine($"商:{quotient},余数:{remainder}"); 
// 输出:商:3,余数:2

在 C# 中,++(递增)和 --(递减)是算术运算符,用于增加或减少变量的值。它们可以作为前缀或后缀运算符使用,这会影响它们在表达式中的行为。


关系运算符

下表显示了 C# 支持的所有关系运算符。假设变量 A 的值为 10,变量 B 的值为 20

运算符描述实例
==检查两个操作数的值是否相等,如果相等则条件为真。(A == B) 不为真。
!=检查两个操作数的值是否相等,如果不相等则条件为真。(A != B) 为真。
>检查左操作数的值是否大于右操作数的值,如果是则条件为真。(A > B) 不为真。
<检查左操作数的值是否小于右操作数的值,如果是则条件为真。(A < B) 为真。
>=检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。(A >= B) 不为真。
<=检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。(A <= B) 为真。

1. 相等运算符 ( == )

用途

  • 用于检查两个操作数是否相等。
int a = 5;
int b = 5;
bool isEqual = (a == b); // isEqual 的值为 true


string str1 = "Hello";
string str2 = "hello";
bool isEqualString = (str1 == str2); 
// isEqualString 的值为 false,因为字符串区分大小写

注意事项

  • 对于引用类型(如对象),== 运算符比较的是对象的引用是否相同,而不是对象的内容是否相同。

    • 1. 值类型和引用类型:

      • 值类型

        • 值类型变量直接存储数据本身。

        • 例如:intdoublebool 等。

        • 当将一个值类型变量赋值给另一个变量时,会复制数据。

        • 每个变量都拥有自己的数据副本,修改一个变量不会影响另一个变量。

      • 引用类型

        • 引用类型变量存储的是数据的内存地址(引用)。

        • 例如:stringobject、数组、类等。

        • 当将一个引用类型变量赋值给另一个变量时,会复制引用(内存地址),而不是数据本身。

        • 多个变量可能引用同一个内存地址,修改其中一个变量会影响其他变量。

    • 2.==运算符的行为:

      • 对于值类型

        • == 运算符比较的是两个变量的值是否相等。
      • 对于引用类型

        • == 运算符比较的是两个变量的引用(内存地址)是否相同。

        • 也就是说,它检查两个变量是否指向内存中的同一个对象。

    • 3. 举例说明:

class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}


Person person1 = new Person { Name = "Alice", Age = 30 };
Person person2 = new Person { Name = "Alice", Age = 30 };
Person person3 = person1;
// person3 = person1; 
// 这条语句确实是将 person1 的内存地址(也就是栈上的地址)赋值给了 person3。


// person1 和 person3 都是引用变量: 
// 它们都存储了堆上对象的内存地址
// 这些变量本身存储在栈上。
// 赋值操作: 
// person3 = person1; 这条语句复制了 person1 变量中存储的内存地址。
// 这个内存地址指向堆上 Person 对象的实际数据
// 因此,person3 变量现在也存储了相同的内存地址,指向堆上的同一个 Person 对象。
// 结果: 
// person1 和 person3 现在都指向堆上的同一个对象


Console.WriteLine(person1 == person2); // 输出:False
Console.WriteLine(person1 == person3); // 输出:True


person1 == person2 的结果为 False
虽然 person1 和 person2 具有相同的内容(Name 和 Age),但它们是内存中的两个不同对象。
person1和person2存储的是不同的内存地址。
因此,== 运算符比较的是它们的引用,结果为 False。


person1 == person3 的结果为 True:person3 = person1; 
语句将 person1 的引用赋值给 person3。
因此,person1 和 person3 引用内存中的同一个对象。
person1和person3存储的是相同的内存地址。
因此,== 运算符比较的是它们的引用,结果为 True。


  • 4. 比较引用类型的内容:

    • 如果需要比较引用类型的内容是否相等,通常需要重写 Equals() 方法。

    • 例如,string 类型重写了 Equals() 方法,因此可以使用 string.Equals() 方法比较字符串的内容。

  • **要比较字符串的内容,可以使用****string.Equals()**方法。


2. 不相等运算符 ( != )

用途

  • 用于检查两个操作数是否不相等。
int a = 5;
int b = 3;
bool isNotEqual = (a != b); // isNotEqual 的值为 true

---

3. 大于运算符 ( > )

用途

  • 用于检查左操作数是否大于右操作数。
int a = 10;
int b = 5;
bool isGreater = (a > b); // isGreater 的值为 true

---

4. 小于运算符 ( < )

用途

  • 用于检查左操作数是否小于右操作数。
int a = 3;
int b = 7;
bool isLess = (a < b); // isLess 的值为 true

---

5. 大于等于运算符 ( >=)

用途

  • 用于检查左操作数是否大于或等于右操作数。
int a = 5;
int b = 5;
bool isGreaterOrEqual = (a >= b); // isGreaterOrEqual 的值为 true

---

6. 小于等于运算符 ( <= )

用途

  • 用于检查左操作数是否小于或等于右操作数。
int a = 3;
int b = 3;
bool isLessOrEqual = (a <= b); // isLessOrEqual 的值为 true

关系运算符的特点

  • 关系运算符的结果都是布尔值(truefalse)。

  • 关系运算符通常用于条件语句(如 if 语句)和循环语句(如 while 语句)中,用于控制程序的流程。

  • 关系运算符可以用于比较数值类型、字符类型和布尔类型。

  • 对于引用类型,关系运算符比较的是引用,而不是对象的内容。

关系运算符的注意事项

  • 在比较浮点数时,由于精度问题,不要直接使用 == 运算符。

  • 应该使用一个小的误差范围来比较浮点数是否近似相等。

  • 在比较字符串时,要注意字符串的大小写。


逻辑运算符

在 C# 中,逻辑运算符用于组合或反转布尔表达式,它们在条件语句和循环中起着至关重要的作用。
下表显示了 C# 支持的所有逻辑运算符。假设变量 A 为布尔值 true,变量 B 为布尔值 false,则:

运算符描述实例
&&称为逻辑与运算符。如果两个操作数都非零,则条件为真。(A && B) 为假。
!称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。!(A && B) 为真。

1. 逻辑与运算符 ( && )

用途

  • 当且仅当两个操作数都为true结果才为true

  • 也称为“短路与”运算符,即如果第一个操作数为 false,则不会计算第二个操作数。

bool a = true;
bool b = false;
bool result1 = a && b; // result1 的值为 false
bool c = true;
bool d = true;
bool result2 = c && d; // result2 的值为 true

---

2. 逻辑或运算符 ( || )

用途

  • 当两个操作数中至少有一个为true结果为true

  • 也称为“短路或”运算符,即如果第一个操作数为 true,则不会计算第二个操作数。

bool a = true;
bool b = false;
bool result3 = a || b; // result3 的值为 true
bool c = false;
bool d = false;
bool result4 = c || d; // result4 的值为 false

---

3. 逻辑非运算符 ( ! )

用途

  • 用于反转操作数的布尔值。

  • 如果操作数为 true,则结果为 false;如果操作数为 false,则结果为 true

bool a = true;
bool result5 = !a; // result5 的值为 false
bool b = false;
bool result6 = !b; // result6 的值为 true

---

4. 逻辑异或运算符 ( ^ )

用途

  • 当两个操作数中只有一个为 true 时,结果为 true

  • 如果两个操作数都为 true 或都为 false,则结果为 false

bool a = true;
bool b = false;
bool result7 = a ^ b; // result7 的值为 true


bool c = true;
bool d = true;
bool result8 = c ^ d; // result8 的值为 false


bool e = false;
bool f = false;
bool result9 = e ^ f; // result9 的值为 false


逻辑运算符的特点

  • 逻辑运算符的操作数和结果都是布尔值truefalse)。

  • 逻辑运算符通常用于组合多个条件,以创建更复杂的条件表达式。

  • 短路运算符 &&|| 可以提高代码的效率,因为它们可以避免不必要的计算。

逻辑运算符的注意事项

  • 逻辑运算符的优先级 < 关系运算符。

  • 可以使用括号**( )** 来改变运算顺序。

逻辑运算符的应用场景

  • **条件判断:**在 if 语句中,可以使用逻辑运算符组合多个条件。

  • **循环控制:**在 while 循环中,可以使用逻辑运算符控制循环的执行。

  • **数据验证:**可以使用逻辑运算符验证用户输入的数据是否满足多个条件。


位运算符

位运算符是编程中用于操作二进制位的运算符。它们直接作用于整数的二进制表示形式,执行逐位操作。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

1. 按位与 ( & )

  • 定义: 如果两个相应的二进制位都为 1,则结果为 1,否则为 0。
int a = 5;  // 二进制:0101
int b = 3;  // 二进制:0011
int result = a & b; // 二进制:0001 (十进制:1)

2. 按位或 ( | )

  • 定义: 如果两个相应的二进制位中至少有一个为 1,则结果为 1,否则为 0。两个位都为0时,结果才为0
int a = 5;  // 二进制:0101
int b = 3;  // 二进制:0011
int result = a | b; // 二进制:0111 (十进制:7)

3. 按位异或 ( ^ )

  • 定义: 如果两个相应的二进制位不同,则结果为 1,否则为 0。
int a = 5;  // 二进制:0101
int b = 3;  // 二进制:0011
int result = a ^ b; // 二进制:0110 (十进制:6)

4. 按位取反 ( ~ )

  • 定义: 将每个二进制位取反,即 0 变为 1,1 变为 0。
int a = 5;  // 二进制:0101
int result = ~a; // 二进制:1010 (十进制:-6)
  • 注意: 由于整数在计算机中通常以补码形式存储,因此对正数取反会得到其负数的补码。

5. 左移 ( << )

  • 定义: 将一个数的二进制位向左移动指定的位数。右侧空出的位用 0 填充。
int a = 5;  // 二进制:0101
int result = a << 2; // 二进制:010100 (十进制:20)


x << y = x * 2^y
  • 相当于: 将原数乘以 2 的移动位数次幂。

6. 右移 ( >> )

定义: 将一个数的二进制位向右移动指定的位数。

  • 对于无符号数: 左侧空出的位用 0 填充。

  • 对于有符号数: 左侧空出的位用符号位填充(正数用 0,负数用 1)。

int a = 20; // 二进制:010100
int result = a >> 2; // 二进制:000101 (十进制:5)
  • 相当于: 将原数除以 2 的移动位数次幂。

应用场景

位运算符在以下场景中非常有用:

  • 底层编程: 操作硬件寄存器、网络协议等。

  • 性能优化: 位运算通常比算术运算更快。

  • 标志和掩码: 使用二进制位来表示状态或选项。

  • 数据加密和解密: 某些加密算法使用位运算。


条件运算符(?:)

  • 定义

    • 三元运算符

    • 条件运算符是一种简洁的 if-else 语句的替代方案。

    • 它允许您根据条件表达式的结果选择两个值中的一个。

    • 语法:条件表达式 ? 表达式1 : 表达式2

    • 如果条件表达式为 true,则返回 表达式1 的结果;否则,返回 表达式2 的结果。

int age = 20;
string message = (age >= 18) ? "成年人" : "未成年人";
Console.WriteLine(message); // 输出:成年人


int temperature = 25;
string weather = (temperature > 30) ? "炎热" : "舒适";
Console.WriteLine(weather); // 输出:舒适
  • 应用场景:

    • 简化简单的 if-else 语句。

    • 在赋值语句中根据条件选择不同的值。

    • 提高代码的简洁性。


空合并运算符

空合并运算符 ?? 是 C# 中一个非常有用的运算符,它提供了一种简洁的方式来处理可能为 null 的变量。
1. 定义

  • 空合并运算符 ?? 用于在变量为 null 时提供一个默认值。

  • 语法:表达式1 ?? 表达式2

  • 如果 表达式1 的值为 null,则返回 表达式2 的结果;否则,返回 表达式1 的结果。

  • 简化 null 检查,并提供一种简洁的方式来为可能为 null 的变量提供默认值。

2. 工作原理

?? 运算符的工作方式是:

  • 首先,它评估左侧的 表达式1

  • 如果 表达式1 的结果不是 null,则 ?? 运算符返回 表达式1 的结果,并且不再评估右侧的 表达式2

  • 如果 表达式1 的结果是 null,则 ?? 运算符评估右侧的 表达式2,并返回 表达式2 的结果。

// 由于 name 为 null,因此 displayName 被赋值为 "未知用户"
string name = null;
string displayName = name ?? "未知用户";
Console.WriteLine(displayName); // 输出:未知用户


// 由于nullableValue为null,因此value被赋值为0。
int? nullableValue = null;
int value = nullableValue ?? 0;
Console.WriteLine(value); // 输出:0


// greeting不为null,所以message被赋值为greeting的值“Hello”
string greeting = "Hello";
string message = greeting ?? "No greeting";
Console.WriteLine(message); // 输出:Hell

4. 应用场景

  • 提供默认值: 当您需要为可能为 null 的变量提供默认值时,可以使用 ?? 运算符。

  • 简化null检查:?? 运算符可以简化 null 检查,使代码更简洁。

  • 提高代码的健壮性: 通过使用 ?? 运算符,您可以确保变量始终具有一个有效的值,从而提高代码的健壮性。

5. 与空合并赋值运算符??=的区别

  • C# 8.0 引入了空合并赋值运算符 ??=

  • ??= 运算符仅在左侧操作数为 null 时才将右侧操作数的值赋给左侧操作数。

  • 例如:

string text = null;
text ??= "default"; // text 现在是 "default"

---

值运算符

运算符描述实例
=简单的赋值运算符,把右边操作数的值赋给左边操作数C = A + B 将把 A + B 的值赋给 C
+=加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数C += A 相当于 C = C + A
-=减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数C -= A 相当于 C = C - A
*=乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数C *= A 相当于 C = C * A
/=除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数C /= A 相当于 C = C / A
%=求模且赋值运算符,求两个操作数的模赋值给左边操作数C %= A 相当于 C = C % A
<<=左移且赋值运算符C <<= 2 等同于 C = C << 2
>>=右移且赋值运算符C >>= 2 等同于 C = C >> 2
&=按位与且赋值运算符C &= 2 等同于 C = C & 2
^=按位异或且赋值运算符C ^= 2 等同于 C = C ^ 2
=按位或且赋值运算符

赋值运算符在编程中用于将值赋给变量。C# 提供了多种赋值运算符,包括简单的赋值和复合赋值。

简单赋值运算符 ( = )

  • 定义

    • 将右侧操作数的值赋给左侧操作数。
  • 语法

    • 变量 = 表达式
int x = 10;
string name = "John";
bool isTrue = true;
等号=
MyClass objectA=new MyClass();
MyClass objectB=objectA;                 
//引用变量的赋值 赋值操作完成后,两个变量都指向同一内存地址


objectA.val=10;                               
//给objectA.val赋值=10 由于objectB和objectA指向同一内存地址,所以ojbectB.val的值也为10


objectB.val=20;                               
//给objectB.val赋值=20 由于objectB和objectA指向同一内存地址,所以objectA.val的值也为20

复合 赋值运算符
复合赋值运算符将算术运算位运算赋值运算结合在一起,简化代码。

加法赋值 ( += )

  • 将右侧操作数的值加到左侧操作数,并将结果赋给左侧操作数

  • 相当于:变量 = 变量 + 表达式

int a = 5;
a += 3; // a 现在是 8 (相当于 a = a + 3)

减法赋值 ( -= )

  • 从左侧操作数中减去右侧操作数的值,并将结果赋给左侧操作数

  • 相当于:变量 = 变量 - 表达式

int b = 10;
b -= 4; // b 现在是 6 (相当于 b = b - 4)

乘法赋值 ( *= )

  • 将左侧操作数乘以右侧操作数的值,并将结果赋给左侧操作数

  • 相当于:变量 = 变量 * 表达式

int c = 2;
c *= 6; // c 现在是 12 (相当于 c = c * 6)

除法赋值 ( /= )

  • 将左侧操作数除以右侧操作数的值,并将结果赋给左侧操作数。

  • 相当于:变量 = 变量 / 表达式

double d = 15;
d /= 3; // d 现在是 5 (相当于 d = d / 3)

取模赋值 ( %= )

  • 将左侧操作数除以右侧操作数的余数赋给左侧操作数。

  • 相当于:变量 = 变量 % 表达式

int e = 17;
e %= 5; // e 现在是 2 (相当于 e = e % 5)

按位与赋值 ( &= )

  • 对左侧和右侧操作数执行按位与运算,并将结果赋给左侧操作数。

  • 相当于:变量 = 变量 & 表达式

int f = 5; // 二进制:0101
f &= 3; // 二进制:0011,f 现在是 1 (相当于 f = f & 3)

按位或赋值 ( |= )

  • 对左侧和右侧操作数执行按位或运算,并将结果赋给左侧操作数。

  • 相当于:变量 = 变量 | 表达式

int g = 5; // 二进制:0101
g |= 3; // 二进制:0011,g 现在是 7 (相当于 g = g | 3)

按位异或赋值 ( ^= )

  • 对左侧和右侧操作数执行按位异或运算,并将结果赋给左侧操作数。

  • 相当于:变量 = 变量 ^ 表达式

int h = 5; // 二进制:0101
h ^= 3; // 二进制:0011,h 现在是 6 (相当于 h = h ^ 3)
  • 左移赋值 ( <<= )

    • 将左侧操作数左移右侧操作数指定的位数,并将结果赋给左侧操作数。

    • 相当于:变量 = 变量 << 表达式

int i = 2; // 二进制:0010
i <<= 3; // 二进制:10000,i 现在是 16 (相当于 i = i << 3)
  • 右移赋值 ( >>= )

    • 将左侧操作数右移右侧操作数指定的位数,并将结果赋给左侧操作数。

    • 相当于:变量 = 变量 >> 表达式

int j = 16; // 二进制:10000
j >>= 2; // 二进制:00100,j 现在是 4 (相当于 j = j >> 2)

---

其他运算符

sizeof()返回数据类型的大小。sizeof(int),将返回 4.
typeof()返回 class 的类型。typeof(StreamReader);
&返回变量的地址。&a; 将得到变量的实际地址。
*变量的指针。*a; 将指向一个变量。
? :条件表达式如果条件为真 ? 则为 X : 否则为 Y
is判断对象是否为某一类型。If( Ford is Car) // 检查 Ford 是否是 Car 类的一个对象。
as强制转换,即使转换失败也不会抛出异常。Object obj = new StringReader(“Hello”);

sizeof()

1. 定义

  • sizeof() 运算符用于获取值类型的大小(以字节为单位)。

  • 它只能用于值类型,不能用于引用类型。

  • 语法:sizeof(类型)

2. 用法和示例

using System;


public class SizeofExample
{
    public static void Main(string[] args)
    {
        Console.WriteLine($"sizeof(int) = {sizeof(int)}");
        Console.WriteLine($"sizeof(short) = {sizeof(short)}");
        Console.WriteLine($"sizeof(long) = {sizeof(long)}");
        Console.WriteLine($"sizeof(float) = {sizeof(float)}");
        Console.WriteLine($"sizeof(double) = {sizeof(double)}");
        Console.WriteLine($"sizeof(char) = {sizeof(char)}");
        Console.WriteLine($"sizeof(bool) = {sizeof(bool)}");
        Console.WriteLine($"sizeof(decimal) = {sizeof(decimal)}");
        Console.WriteLine($"sizeof(byte) = {sizeof(byte)}");
        Console.WriteLine($"sizeof(sbyte) = {sizeof(sbyte)}");
    }
}


sizeof(int) = 4
sizeof(short) = 2
sizeof(long) = 8
sizeof(float) = 4
sizeof(double) = 8
sizeof(char) = 2
sizeof(bool) = 1
sizeof(decimal) = 16
sizeof(byte) = 1
sizeof(sbyte) = 1
  • 此代码将输出各种值类型的大小。

  • 请注意,sizeof() 运算符只能在 unsafe 上下文中使用,或者使用非托管代码

  • 即使使用sizeof(), 对基本数值类型,编译器在编译时就能处理,并不需要进入unsafe上下文。

3. 注意事项

  • sizeof() 运算符返回的大小可能因平台和编译器而异。

  • 在 C# 中,sizeof() 运算符通常用于与非托管代码交互或执行底层操作。

4. 为什么要注意数据类型大小

  • 内存管理: 了解数据类型的大小有助于优化内存使用。

  • 数据结构: 在设计数据结构时,了解数据类型的大小至关重要。

  • 互操作性: 当与非托管代码或外部系统交互时,数据类型的大小很重要。


typeof()

用于获取类型的 System.Type 对象。System.Type 对象包含了类型的元数据信息,例如类型的名称、命名空间、基类、接口、属性、方法等。
定义:

  • typeof 运算符接受一个类型作为参数,并返回该类型的 System.Type 对象。

  • 语法:typeof(类型)

用法及示例:

using System;
using System.IO;


public class TypeofExample
{
    public static void Main(string[] args)
    {
        Type intType = typeof(int);
        Type stringType = typeof(string);
        Type streamReaderType = typeof(StreamReader);


        Console.WriteLine($"intType: {intType}");
        Console.WriteLine($"stringType: {stringType}");
        Console.WriteLine($"streamReaderType: {streamReaderType}");


        // 获取类型的元数据信息
        Console.WriteLine($"StreamReader's FullName: {streamReaderType.FullName}");
        Console.WriteLine($"StreamReader's Namespace: {streamReaderType.Namespace}");
        Console.WriteLine($"StreamReader's BaseType: {streamReaderType.BaseType}");
    }
}


intType: System.Int32
stringType: System.String
streamReaderType: System.IO.StreamReader
StreamReader's FullName: System.IO.StreamReader
StreamReader's Namespace: System.IO
StreamReader's BaseType: System.IO.TextReader
  • 此代码演示了如何使用 typeof 运算符获取 intstringStreamReader 类型的 System.Type 对象。

  • 然后,它使用 System.Type 对象的属性来获取 StreamReader 类型的元数据信息。

特点和应用场景

  • 编译时运算符:typeof 运算符在编译时求值,这意味着它不会在运行时产生额外的性能开销。

  • **获取类型信息:**typeof运算符主要用于获取类型的元数据信息,这些信息可以用于:

    • 反射:在运行时动态地创建和调用类型的实例。

    • 类型检查:确定一个对象的类型

    • 序列化和反序列化:将对象转换为字节流或从字节流还原对象。

  • 泛型类型

    • typeof 运算符可以用于泛型类型。例如:typeof(List<int>)
  • 开放泛型类型和封闭泛型类型

    • typeof(List<>)是开放泛型类型。

    • typeof(List<int>)是封闭泛型类型。

System.Type类:

  • System.Type 类是 .NET Framework(或 .NET Core/.NET 5+)中表示类型元数据的类。

  • 它提供了许多属性和方法,用于获取类型的各种信息。

  • 例如,System.Type 类提供了以下属性:

    • FullName:获取类型的完整名称(包括命名空间)。

    • Namespace:获取类型的命名空间。

    • BaseType:获取类型的基类。

    • IsClass:指示类型是否为类。

    • IsInterface:指示类型是否为接口。

    • GetMethods():获取类型的所有公共方法。

    • GetProperties():获取类型的所有公共属性。

总结:

  • typeof 运算符是 C# 中一个非常有用的运算符,它允许你在编译时获取类型的元数据信息。

  • 它通常与 System.Type 类一起使用,以执行反射、类型检查和序列化等操作。


& 运算符 (取地址运算符)

  • 定义

    • &运算符返回变量的内存地址

    • 它只能应用于固定变量。

    • 语法:&变量

unsafe
{
    int a = 10;
    int* ptr = &a; // ptr 现在包含变量 a 的内存地址
    Console.WriteLine($"变量 a 的地址:{(long)ptr:X}"); // 输出地址的十六进制表示
}
  • 在这个例子中,&a返回变量 a 的内存地址,并将其赋给指针变量 ptr。

  • 为了输出地址,我们将指针转换为 long,然后使用 X 格式说明符将其格式化为十六进制。

  • 应用场景:

    • 获取变量的内存地址,以便传递给需要指针参数的函数。

    • unsafe 代码块中,直接操作内存。

    • 与非托管代码交互

      • 当 C# 代码需要调用使用 C 或 C++ 编写的非托管代码库时,通常需要传递变量的内存地址。

      • 非托管代码使用指针来操作内存,因此需要使用 & 运算符获取变量的地址,并将其转换为指针类型。

      • 例如,调用 Windows API 函数时,经常需要传递缓冲区(数组)的地址。

    • 性能敏感的底层操作

      • 在某些性能要求极高的场景下,例如图像处理、音视频处理或游戏开发,直接操作内存可以提高效率。

      • 使用 & 运算符和指针,可以绕过 C# 的类型安全检查,直接访问和修改内存中的数据。

    • 硬件编程

      • 当 C# 代码需要与底层硬件交互时,例如驱动程序开发,通常需要直接操作硬件寄存器的内存地址。

      • & 运算符可以用于获取硬件寄存器的内存地址,以便进行读写操作。

    • 固定数组

      • 当在unsafe上下文中使用固定大小的数组时,&运算符可以被用来获得数组的第一个元素的地址。
unsafe
{
    int value = 10;
    int* pointer = &value; // 获取 value 的内存地址


    Console.WriteLine($"Value 的地址:{(long)pointer:X}");
    Console.WriteLine($"Pointer 指向的值:{*pointer}");


    *pointer = 20; // 通过指针修改 value 的值


    Console.WriteLine($"修改后的 Value:{value}");
}

为什么将内存地址转换为 long

  • 地址表示

    • 内存地址本质上是一个数值,用于标识内存中的一个特定位置。

    • 在 64 位操作系统上,内存地址通常用 64 位整数表示,即 long 类型。

    • 在 32 位操作系统上,内存地址通常用 32 位整数表示,即 int 类型。

  • 输出和显示

    • 为了方便查看和调试,通常将内存地址转换为十六进制字符串。

    • long 类型提供了足够的空间来存储 64 位内存地址,并可以使用 X 格式说明符将其格式化为十六进制。

    • 在32位系统上,也可以转换成uint。

  • 指针运算:

    • 在进行指针运算时,例如指针的加减,需要将指针转换为整数类型。

    • long 类型可以确保在 64 位系统上进行正确的指针运算。


* 运算符 (间接寻址运算符/解引用运算符)

  • *定义:运算符用于访问指针指向的内存位置的值

  • 只能应用于指针变量

  • 语法:*指针

unsafe
{
    int a = 10;
    int* ptr = &a;
    Console.WriteLine($"指针 ptr 指向的值:{*ptr}"); // 输出 10
    *ptr = 20; // 修改指针指向的内存位置的值
    Console.WriteLine($"变量 a 的新值:{a}"); // 输出 20
}
  • 在这个例子中,*ptr 返回指针 ptr 指向的内存位置的值(即变量 a 的值)。

  • *ptr = 20; 修改了指针 ptr 指向的内存位置的值,因此变量 a 的值也发生了变化。

  • 应用场景

    • 访问指针指向的内存位置的值。

    • 修改指针指向的内存位置的值。

    • unsafe 代码块中,直接操作内存。

  • 注意事项

    • & 和 * 运算符只能在 unsafe 上下文中使用。

    • 使用指针需要格外小心,因为错误的操作可能导致内存损坏、崩溃和安全漏洞。

    • 指针操作通常用于与非托管代码交互或执行底层操作。

  • 总结

    • & 运算符返回变量的内存地址。

    • * 运算符访问指针指向的内存位置的值。

    • 这两个运算符在 unsafe 上下文中用于直接操作内存。


X"格式说明符用于将数值格式化为十六进制字符串。以下是关于"X"格式说明符的详细解释:

1. 定义

  • "X"格式说明符用于将整数类型(如intlong等)的数值转换为十六进制字符串表示形式。

它有两种形式:“X”(大写)和"x"(小写)。

  • “X”:输出大写十六进制字符(A-F)。

  • “x”:输出小写十六进制字符(a-f)。

2. 用法和示例

C#

using System;


public class HexFormatExample
{
    public static void Main(string[] args)
    {
        int number = 255;


        Console.WriteLine($"Number (Decimal): {number}");
        Console.WriteLine($"Number (Hexadecimal, Uppercase): {number:X}");
        Console.WriteLine($"Number (Hexadecimal, Lowercase): {number:x}");


        long address = 0x1A2B3C4D5E6F7890;
        Console.WriteLine($"Address (Hexadecimal): {address:X}");
    }
}

在这个例子中:

  • {number:X} 将整数 number(255)格式化为大写十六进制字符串 “FF”。

  • {number:x} 将整数 number(255)格式化为小写十六进制字符串 “ff”。

  • {address:X} 将long类型的address变量格式化为大写十六进制字符串。

  • 你还可以指定十六进制字符串的最小宽度。例如,{number:X4} 将确保输出的十六进制字符串至少有 4 位,如果不足,则在前面填充零。

3. 应用场景

  • **内存地址显示:**在调试或底层编程中,经常需要显示内存地址,而十六进制是内存地址的常用表示形式。

  • **数据表示:**某些数据(例如,颜色值、硬件寄存器值)通常以十六进制表示。

  • **网络协议:**在网络协议中,某些数据字段也可能使用十六进制表示。

  • **文件格式:**在二进制文件格式中,经常使用十六进制表示数据。

4. 总结

  • "X"格式说明符提供了一种方便的方法,用于将数值转换为十六进制字符串。

  • 它可以用于显示内存地址、表示数据和在各种编程场景中进行调试。

类别运算符结合性
后缀() [] -> . ++ - -从左到右
一元+ - ! ~ ++ - - (type)* & sizeof从右到左
乘除* / %从左到右
加减+ -从左到右
移位<< >>从左到右
关系< <= > >=从左到右
相等== !=从左到右
位与 AND&从左到右
位异或 XOR^从左到右
位或 OR
逻辑与 AND&&从左到右
逻辑或 OR
条件?:从右到左
赋值= += -= *= /= %=>>= <<= &= ^==
逗号,从左到右

GetType

GetType()System.Object 类的一个方法,因此所有对象都继承了它。它用于在运行时获取对象的 System.Type 对象,该对象包含了对象的元数据信息。
1. 定义

  • GetType() 方法返回一个 System.Type 对象,该对象表示对象的运行时类型。

  • 语法:对象.GetType()

using System;
using System.IO;


public class GetTypeExample
{
    public static void Main(string[] args)
    {
        string text = "Hello, world!";
        int number = 42;
        StreamReader reader = new StreamReader("example.txt");


        Type textType = text.GetType();
        Type numberType = number.GetType();
        Type readerType = reader.GetType();


        Console.WriteLine($"textType: {textType}");
        Console.WriteLine($"numberType: {numberType}");
        Console.WriteLine($"readerType: {readerType}");


        // 获取类型的元数据信息
        Console.WriteLine($"readerType's FullName: {readerType.FullName}");
        Console.WriteLine($"readerType's Namespace: {readerType.Namespace}");
        Console.WriteLine($"readerType's BaseType: {readerType.BaseType}");


        reader.Close();
    }
}
  • 在这个例子中,text.GetType() 返回 string 类型的 System.Type 对象,number.GetType() 返回 int 类型的 System.Type 对象,reader.GetType() 返回 StreamReader 类型的 System.Type 对象。

  • 然后,它使用 System.Type 对象的属性来获取 StreamReader 类型的元数据信息

**GetType() 与 typeof()**的区别

  • GetType()**:**是一个实例方法,在运行时获取对象的类型。

  • 适用于获取对象的运行时类型,特别是当对象的类型在编译时未知时。

  • typeof()**:**是一个运算符,在编译时获取类型的 System.Type 对象。

  • 适用于获取已知类型的元数据信息。

  • 语法:typeof(类型)

应用场景

  • 运行时类型检查

    • 使用 GetType() 方法可以确定对象的运行时类型,并根据类型执行不同的操作。
  • 反射

    • GetType() 方法是反射的基础,它允许你在运行时获取对象的类型信息,并使用这些信息来动态地创建和调用类型的实例。
  • 多态

    • 在多态编程中,可以使用 GetType() 方法来确定对象的实际类型,并根据类型执行特定的操作。
  • 动态加载类型

    • 当你需要动态加载类型时,可以使用GetType()获取类型信息。

总结

  • GetType() 方法是一个运行时方法,用于获取对象的 System.Type 对象。

  • 它在运行时类型检查、反射和多态等场景中非常有用。

  • GetType()typeof() 都是获取 System.Type 对象的方法,但它们的使用场景和求值时间不同。


asis 都是用于类型检查和转换的运算符,但它们之间存在一些关键区别。

is 运算符

  • 定义

    • is 运算符用于检查对象是否与给定类型兼容。

    • 如果对象是指定类型或可以隐式转换为指定类型,则 is 运算符返回 true;否则,返回 false

    • is 运算符不会引发异常。

object obj = "Hello";


if (obj is string)
{
    Console.WriteLine("obj is a string.");
}


if (obj is int)
{
    Console.WriteLine("obj is an integer."); // 不会执行
}


if (obj is object)
{
    Console.WriteLine("obj is an object."); // 会执行
}
  • 应用场景

    • 在执行类型转换之前,使用 is 运算符可以避免 InvalidCastException 异常。

    • 在多态编程中,可以使用 is 运算符来确定对象的实际类型,并根据类型执行特定的操作。


as 运算符

  • 定义

    • as 运算符用于将对象显式转换为指定类型。

    • 如果对象可以转换为指定类型,则 as 运算符返回转换后的对象;否则,返回 null

    • as 运算符不会引发异常。

object obj = "Hello";


string str = obj as string;


if (str != null)
{
    Console.WriteLine($"str: {str}");
}


int? num = obj as int?; // num 为 null


if (num == null)
{
    Console.WriteLine("obj cannot be converted to int.");
}
  • 应用场景

    • as 运算符通常用于将对象转换为引用类型或可为空的值类型。

    • is 运算符结合使用,可以实现安全的类型转换。

as 和 is 的区别

  • is 运算符只进行类型检查,不进行类型转换;as 运算符进行类型转换,并在转换失败时返回 null

  • is 运算符可以用于检查对象是否与任何类型兼容,包括值类型和引用类型;as 运算符主要用于引用类型和可为空的值类型。

  • is 运算符返回布尔值(truefalse);as 运算符返回转换后的对象或 null

using System;
using System.Collections;


public class AsIsExample
{
    public static void Main(string[] args)
    {
        ArrayList list = new ArrayList();
        list.Add("Hello");
        list.Add(42);


        foreach (object item in list)
        {
            if (item is string)
            {
                string str = item as string;
                Console.WriteLine($"String: {str}");
            }
            else if (item is int)
            {
                int num = (int)item; // 使用强制类型转换
                Console.WriteLine($"Integer: {num}");
            }
        }


        object obj1 = new object();
        string str1 = obj1 as string; // str1 为 null


        if (str1 == null)
        {
            Console.WriteLine("obj1 cannot be converted to string.");
        }


        object obj2 = "World";


        if (obj2 is IDisposable)
        {
            IDisposable disposable = obj2 as IDisposable; // disposable 为 null,因为 string 没有实现 IDisposable
            if (disposable != null)
            {
                disposable.Dispose();
            }
        }


        if (obj2 is string)
        {
            string str2 = (string)obj2;
            Console.WriteLine(str2);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值