1. 属性和 Backing Field#

1.1. Backing Field#

在 C# 中, 当你创建一个类的属性时, 你实际上是在创建一种访问类内部数据的方式, 而这个内部数据就存储在 backing field 中:

public class Person {
    // 这是 backing field
    private string _name;
    
    // 这是属性
    public string Name {
        get { return _name; }
        set { _name = value; }
    }
}

对于backing field的命名, C#中有几种常见的约定:

private string _name; // 对应 Name 属性
private string m_name; // 对应 Name 属性, m 代表 member
private string name; // 对应 Name 属性

微软官方推荐使用下划线前缀(⁠_name)的命名方式,这也是.NET社区中最广泛采用的规范。

1.2. Auto-implemented Properties#

C# 3.0引入了自动实现的属性, 编译器会自动生成 backing field:

public class Person {
    // 编译器会自动为属性 Name 生成一个 backing field
    public string Name { get; set; }
}

常见的例子:

public class Person {
    private int _age;
    
    public int Age {
        get { return _age; }
        set { 
            if (value < 0)
                throw new ArgumentException("Age cannot be negative");
            _age = value; 
        }
    }
}

Backing Field 是一个私有字段, 用于存储属性的实际数据

⁠value 是 C# 中的一个特殊关键字, 它只能在属性或索引器的 ⁠set 访问器中使用, 它代表调用者尝试分配给属性的值

Person person = new Person();
person.Name = "John"; // 这里的 "John" 就是 set 访问器中的 value

1.3. 只声明 get 访问器#

Declare only the [get] accessor, which makes the property immutable everywhere except in the type’s constructor.

public class Person
{
    public string Name { get; } // 只声明 get

    public Person(string name)
    {
        Name = name; // 只能在构造函数中设置
    }
}

var person = new Person("Alice");
// person.Name = "Bob"; // 错误!无法修改,因为没有 set
Console.WriteLine(person.Name); // 输出: Alice

这种方式适合需要保护数据不被外部修改的场景, 比如表示一个对象的固有属性(例如身份证号)

2. 构造函数#

2.1. 对象初始化器#

在 C# 中, 如果一个类没有显式定义任何构造函数, 编译器会自动为该类提供一个 无参的默认构造函数, 这个默认构造函数的作用是创建一个类的实例, 并将所有字段或属性初始化为它们的默认值(例如,string 初始化为 null,int 初始化为 0)

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

    public void SayHello() {
        Console.WriteLine($"Hello, I am {Name}, {Age} years old.");
    }
}

Person p = new Person { Name = "Bob", Age = 30 };

可是这里是什么意思呢, 创建实例的时候不是传了参数吗?

new Person() 确实调用了默认构造函数, 创建一个 Person 对象, 此时 Namenull, Age0, 但紧随其后的 { Name = "Bob", Age = 30 }对象初始化器 的语法, 它允许你在对象创建后立即设置属性的值, 这实际上是 C# 提供的一种简洁写法, 相当于以下代码:

Person p = new Person(); // 调用默认构造函数
p.Name = "Bob";          // 设置 Name 属性
p.Age = 30;              // 设置 Age 属性

对象初始化器的工作原理是:

  1. 先调用类的构造函数(这里是隐式的默认构造函数)
  2. 然后按照初始化器中指定的顺序, 依次设置对象的公共属性或字段