在 C# 中,属性(Property) 是类或结构体中的成员,用于封装对私有字段(称为 backing field)的访问,提供更灵活和安全的数据操作方式。属性通过 get 和 set 访问器控制对数据的读写,是面向对象编程中封装思想的核心实践。
属性的核心作用
- 封装数据:隐藏类的内部字段,通过属性暴露可控的访问接口。
- 验证逻辑:在 set 访问器中添加数据验证逻辑。
- 计算属性:动态生成值(例如根据其他字段计算)。
- 简化语法:自动属性(Auto-Implemented Properties)让代码更简洁。
属性的基本语法
public class Person
{// 私有字段(backing field)private string _name;// 属性:控制对 _name 的读写public string Name{get { return _name; }set { _name = value?.Trim(); } // 示例:自动去除空格}
}
属性的常见类型
1. 自动实现属性(Auto-Implemented Property)
不需要手动定义 backing field,编译器自动生成。
public int Age { get; set; } // 默认可读写
public string Id { get; } // 只读属性(只能在构造函数中赋值)
2. 计算属性(Computed Property)
根据其他字段动态返回值:
public class Rectangle
{public double Width { get; set; }public double Height { get; set; }// 计算面积(只读)public double Area => Width * Height;
}
3. 带有验证的属性
在 set 访问器中添加逻辑:
private int _age;
public int Age
{get => _age;set{if (value < 0)throw new ArgumentException("年龄不能为负数!");_age = value;}
}
4. 表达式体属性(C# 6+)
简化单行逻辑的属性:
public string FullName => $"{FirstName} {LastName}";
5. Init-only 属性(C# 9+)
只能在对象初始化时赋值:
public string Name { get; init; }
// 使用:
var person = new Person { Name = "Alice" }; // 合法
person.Name = "Bob"; // 编译错误!
属性 vs 字段
字段(Field) | 属性(Property) |
---|---|
直接暴露数据,无访问控制。 | 封装数据,通过 get/set 控制访问。 |
无法添加验证或逻辑。 | 支持数据验证、动态计算等。 |
例如:public string Name; | 例如:public string Name { get; set; } |
最佳实践
1、优先使用属性:避免直接暴露公有字段。
2、保持属性简单:避免在属性中编写复杂逻辑(应交给方法)。
3、只读属性:若数据不应被外部修改,使用 { get; private set; } 或 init。
4、线程安全:在属性中处理多线程访问时需加锁。
示例:完整用法
public class User
{private string _email;public string Email{get => _email;set{if (string.IsNullOrEmpty(value) || !value.Contains("@"))throw new ArgumentException("邮箱格式无效!");_email = value;}}// 自动属性 + 默认值public DateTime CreatedAt { get; } = DateTime.Now;
}// 使用:
var user = new User();
user.Email = "test@example.com"; // 合法
user.Email = "invalid"; // 抛出异常
掌握属性是 C# 开发的基础,它能显著提升代码的可维护性和健壮性。