1. 类的基本概念与核心特性
在C#编程语言中,类(class)是面向对象编程(OOP)的基础构建块,它定义了一组相关数据和行为的蓝图。类本质上是一种自定义数据类型,它封装了字段(数据成员)和方法(函数成员),形成一个逻辑单元。理解类的概念对于掌握C#编程至关重要,因为.NET框架本身就是建立在大量相互关联的类库之上的。
类的核心特性包括:
- 封装性:将数据和行为捆绑在一起,并控制外部对内部实现的访问
- 继承性:允许创建层次结构,子类可以继承父类的特征
- 多态性:同一操作在不同类中有不同实现方式
- 抽象性:可以定义抽象类和接口,只声明不实现
在C#中声明类的基本语法非常简单:
[访问修饰符] class 类名 { // 字段(数据成员) // 属性 // 方法 // 事件 // 构造函数 // 析构函数 // 索引器 // 运算符 // 嵌套类型 }2. 类的成员构成详解
2.1 字段与属性
字段是类中存储数据的变量,而属性则是字段的访问器,提供了对字段的安全访问机制。在C#中,我们通常不会直接暴露字段,而是通过属性来访问:
public class Person { // 私有字段(通常以下划线开头) private string _name; // 公共属性 public string Name { get { return _name; } set { if(string.IsNullOrEmpty(value)) throw new ArgumentException("姓名不能为空"); _name = value; } } // 自动实现的属性(编译器会自动生成后台字段) public int Age { get; set; } }属性提供了数据验证、计算属性等功能,是C#中实现封装的重要手段。从C# 7.0开始,我们还可以使用更简洁的表达方式:
public string Name { get => _name; set => _name = value ?? throw new ArgumentNullException(nameof(value)); }2.2 方法与构造函数
方法是类中定义行为的成员。构造函数是一种特殊方法,在创建类实例时自动调用:
public class Rectangle { // 字段 private double _width; private double _height; // 构造函数 public Rectangle(double width, double height) { _width = width; _height = height; } // 方法 public double CalculateArea() { return _width * _height; } // 静态方法 public static Rectangle CreateSquare(double side) { return new Rectangle(side, side); } }C#支持方法重载(相同方法名不同参数)、可选参数、命名参数等特性。从C# 8.0开始,还支持默认接口方法。
2.3 其他类成员
类还可以包含以下成员:
- 事件:用于实现观察者模式
- 索引器:使对象可以像数组一样被索引
- 运算符重载:定义类实例上的运算符行为
- 嵌套类型:在类内部定义的类型
3. 类的访问控制与修饰符
3.1 访问修饰符
C#提供了多种访问修饰符来控制类及其成员的可见性:
| 修饰符 | 可访问性 |
|---|---|
| public | 无限制访问 |
| private | 仅限包含类内部 |
| protected | 包含类和派生类 |
| internal | 同一程序集内 |
| protected internal | 同一程序集或派生类 |
| private protected | 同一程序集中的派生类 |
3.2 其他重要修饰符
- static:声明静态成员,属于类而非实例
- readonly:声明只能在声明时或构造函数中赋值的字段
- const:编译时常量
- sealed:阻止类被继承
- abstract:抽象类,不能实例化
- partial:允许类定义分散在多个文件中
- virtual/override:支持多态行为
4. 类的继承与多态
4.1 继承基础
继承是OOP的核心概念之一,C#使用单继承模型(一个类只能直接继承一个父类):
public class Shape { public virtual void Draw() { Console.WriteLine("绘制形状"); } } public class Circle : Shape { public override void Draw() { Console.WriteLine("绘制圆形"); } }4.2 多态实现
多态允许通过基类引用调用派生类的方法:
Shape shape = new Circle(); shape.Draw(); // 输出"绘制圆形"C#实现多态主要通过:
- 虚方法(virtual/override)
- 抽象方法(abstract)
- 接口(interface)
4.3 接口与抽象类
接口定义契约而不提供实现,抽象类可以包含部分实现:
public interface IDrawable { void Draw(); } public abstract class GraphicObject : IDrawable { public abstract void Draw(); public void Move(int x, int y) { Console.WriteLine($"移动到位置({x}, {y})"); } }5. 类的实际应用与高级特性
5.1 泛型类
C#支持泛型类,可以创建类型安全的集合和算法:
public class GenericList<T> { private T[] items; private int count; public GenericList(int capacity) { items = new T[capacity]; } public void Add(T item) { if(count == items.Length) Array.Resize(ref items, items.Length * 2); items[count++] = item; } public T this[int index] { get => items[index]; set => items[index] = value; } }5.2 扩展方法
扩展方法允许向现有类添加方法而无需修改原始类:
public static class StringExtensions { public static bool IsNullOrWhiteSpace(this string value) { return string.IsNullOrEmpty(value) || value.Trim().Length == 0; } } // 使用 string test = " "; if(test.IsNullOrWhiteSpace()) { Console.WriteLine("字符串为空或空白"); }5.3 记录类(C# 9.0+)
记录(record)是一种特殊的类,主要用于存储数据:
public record Person(string FirstName, string LastName); // 使用 var person1 = new Person("张", "三"); var person2 = person1 with { LastName = "四" };记录类自动实现值相等性比较、ToString()等方法,适合不可变数据模型。
5.4 模式匹配(C# 7.0+)
C#增强了模式匹配能力,可以更简洁地处理类层次结构:
public static double CalculateArea(object shape) { return shape switch { Circle c => Math.PI * c.Radius * c.Radius, Rectangle r => r.Width * r.Height, _ => throw new ArgumentException("未知形状") }; }6. 类的最佳实践与常见问题
6.1 设计原则
- 单一职责原则:一个类只应有一个引起变化的原因
- 开闭原则:对扩展开放,对修改关闭
- 里氏替换原则:派生类应该能够替换基类
- 接口隔离原则:客户端不应被迫依赖不使用的接口
- 依赖倒置原则:依赖抽象而非具体实现
6.2 常见陷阱
- 过度使用继承:优先考虑组合而非继承
- 忽略对象生命周期:特别是涉及非托管资源时
- 不恰当的访问级别:过度暴露内部实现细节
- 忽略相等性比较:特别是用作字典键或集合元素时
- 线程安全问题:共享状态需要同步机制
6.3 性能考量
- 避免过度细分小类导致的调用开销
- 考虑使用结构体(struct)替代小型不可变类
- 注意装箱/拆箱操作对性能的影响
- 合理使用对象池管理频繁创建销毁的对象
7. 类在实际项目中的应用示例
7.1 WPF中的MVVM模式
public class MainViewModel : INotifyPropertyChanged { private string _userName; public string UserName { get => _userName; set { if(_userName != value) { _userName = value; OnPropertyChanged(); OnPropertyChanged(nameof(Greeting)); } } } public string Greeting => $"你好, {UserName}"; public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }7.2 数据库访问层
public abstract class RepositoryBase<T> where T : class { protected readonly DbContext _context; protected RepositoryBase(DbContext context) { _context = context; } public virtual async Task<T> GetByIdAsync(int id) { return await _context.Set<T>().FindAsync(id); } public virtual async Task AddAsync(T entity) { await _context.Set<T>().AddAsync(entity); await _context.SaveChangesAsync(); } }7.3 多线程编程
public class ThreadSafeQueue<T> { private readonly Queue<T> _queue = new Queue<T>(); private readonly object _lock = new object(); public void Enqueue(T item) { lock(_lock) { _queue.Enqueue(item); Monitor.Pulse(_lock); } } public T Dequeue() { lock(_lock) { while(_queue.Count == 0) Monitor.Wait(_lock); return _queue.Dequeue(); } } }8. 类在C#生态系统中的演进
C#语言版本迭代不断为类添加新特性:
- C# 2.0:泛型、静态类、分部类
- C# 3.0:自动属性、对象初始化器、扩展方法
- C# 4.0:动态类型、命名参数/可选参数
- C# 5.0:异步方法(async/await)
- C# 6.0:表达式体成员、null条件运算符
- C# 7.0:模式匹配、本地函数
- C# 8.0:默认接口方法、可空引用类型
- C# 9.0:记录类、init-only属性
- C# 10.0:记录结构、全局using指令
- C# 11.0:泛型数学支持、required成员
理解这些演进有助于编写更现代、更简洁的C#代码。例如,现代C#中类的声明可以非常简洁:
public record Person(string Name, int Age) { public string Greeting => $"你好,我是{Name},今年{Age}岁"; }