C#语法补充

空条件运算符和空合并运算符

  • var clientIp = context.HttpContext.Connection.RemoteIpAddress?.ToString() ?? "unknown";
- 首先RemoteIpAddress允许为空
- 若这个表达式(context.HttpContext.Connection.RemoteIpAddress?.ToString())值为null,则返回右边的值
  • 类似的语法模式如下
// 示例1:处理可能为null的配置值
var connectionString = Configuration.GetSection("ConnectionStrings:Default")?.Value ?? "DefaultConnection";

// 示例2:处理可能为null的用户输入
var userName = user?.Name ?? "匿名用户";

// 示例3:处理可能为null的集合
var firstItem = items?.FirstOrDefault() ?? defaultItem;

值类型和引用类型的区别

值类型 (Value Types)

特点:

  • 直接存储数据本身
  • 分配在栈(stack)上(但作为类成员时随对象在堆上)
  • 赋值时复制整个值
  • 不能为null(除非使用可空类型)

主要分类:

// 1. 基本数值类型
int number = 10;
double price = 99.99;
bool flag = true;
char character = 'A';

// 2. 结构体 (struct)
struct Point
{
    public int X;
    public int Y;
}

// 3. 枚举 (enum)
enum Color { Red, Green, Blue }

引用类型 (Reference Types)

特点:

  • 存储的是内存地址引用
  • 分配在堆(heap)上
  • 赋值时复制引用,而不是数据本身
  • 可以为null

主要分类:

// 1. 类 (class)
class Person
{
    public string Name;
    public int Age;
}

// 2. 接口 (interface)
interface IAnimal
{
    void Speak();
}

// 3. 数组
int[] numbers = new int[5];
string[] names = new string[3];

// 4. 委托 (delegate)
delegate void MyDelegate(string message);

// 5. 字符串
string text = "Hello World";

核心区别

1. 内存分配和存储

// 值类型示例
int a = 10;
int b = a;  // 复制值
b = 20;     // 不影响a
Console.WriteLine(a); // 输出: 10

// 引用类型示例
Person p1 = new Person { Name = "John" };
Person p2 = p1;       // 复制引用
p2.Name = "Jane";     // 影响p1
Console.WriteLine(p1.Name); // 输出: Jane

2. 参数传递行为

void ModifyValue(int value) => value = 100;
void ModifyReference(Person person) => person.Name = "Modified";

int num = 50;
Person person = new Person { Name = "Original" };

ModifyValue(num);      // num不变,还是50
ModifyReference(person); // person.Name变成"Modified"

3. 默认值不同

int defaultInt;        // 0
bool defaultBool;      // false
Person defaultPerson;  // null

4. 相等比较

// 值类型比较内容
int x = 5, y = 5;
Console.WriteLine(x == y); // True

// 引用类型默认比较引用
Person p1 = new Person { Name = "Tom" };
Person p2 = new Person { Name = "Tom" };
Console.WriteLine(p1 == p2); // False(除非重写==运算符)

特殊情况和注意事项

1. 装箱和拆箱 (Boxing/Unboxing)

// 装箱:值类型 → 引用类型
int value = 42;
object boxed = value;  // 装箱

// 拆箱:引用类型 → 值类型
int unboxed = (int)boxed;  // 拆箱

2. ref 关键字

void ModifyWithRef(ref int value) => value = 100;

int number = 50;
ModifyWithRef(ref number);
Console.WriteLine(number); // 输出: 100

3. 可空值类型

int? nullableInt = null;        // 等价于 Nullable<int>
DateTime? nullableDate = null;

性能考虑

  • 值类型:栈分配,快速,无GC压力
  • 引用类型:堆分配,需要GC管理,有开销

选择指南

使用值类型当:

  • 类型表示单个值
  • 实例大小较小(通常<16字节)
  • 不需要继承
  • 希望避免堆分配

使用引用类型当:

  • 类型是复杂对象
  • 需要继承和多态
  • 实例可能很大
  • 需要共享引用

理解这两种类型的区别对于编写高效、正确的C#代码至关重要,特别是在处理性能敏感场景时。