本文对 C# 类的 系统化、深度且易理解 的讲解,覆盖类的所有核心组成部分(成员、修饰符、静态 / 实例特性、构造析构、关键字等)。以 “概念定义 + 核心特性 + 代码示例 + 使用场景” 的结构,帮你构建完整的类知识体系,同时点明新手易混淆的关键点。
一、类的本质与核心定位 C# 是纯面向对象语言, 类(Class) 是面向对象编程(OOP)的核心载体 —— 它是对 “数据(状态)” 和 “行为(操作)” 的封装,是创建对象(实例)的 “模板”。
// 最基础的类结构:包含数据(字段)和行为(方法) class Phone { // 数据:描述手机的状态(字段) string _brand; int _batteryLevel; // 行为:手机能执行的操作(方法) void Call ( string number ) { } void Charge () { } }
二、类的成员:完整分类与说明 类的成员是类的 “组成部分”,覆盖你提到的所有类型,按功能可分为以下大类,每类都有明确的职责:
表格
字段(Field) private string _name; 方法(Method) public void Run() { } 属性(Property) public string Name { get; set; } 构造函数 public Phone() { } 析构函数 ~Phone() { } 常量(Constant) public const int MaxBattery = 100; 索引器 public string this[int index] { get; } 静态成员 public static int TotalPhones; 其他成员 public event Action OnLowBattery;
关键补充:成员修饰符的 规范顺序 C# 编译器不强制修饰符顺序,但遵循行业规范能提升代码可读性, 推荐顺序 (从左到右):
访问修饰符 → 静态/实例 → 其他修饰符 → 类型/返回值 → 成员名
✅ 正确示例:
// 访问修饰符(public) → 静态(static) → 只读(readonly) → 类型(int) → 成员名(DefaultPort) public static readonly int DefaultPort = 8080 ; // 访问修饰符(private) → 实例(默认) → 类型(string) → 成员名(_ip) private string _ip;
❌ 不推荐(顺序混乱):
static public int Timeout = 30 ; // 静态修饰符在访问修饰符前 private readonly static string Url = "https://example.com" ; // 只读在静态前
三、实例成员 vs 静态成员:核心差异 这是类成员最核心的分类维度,新手最易混淆,需重点理解:
1. 实例类成员(非静态) • 归属 :属于 对象(实例) ,每个实例有独立的副本。 • 访问条件 :必须先通过 new 创建实例,才能访问。 • 访问范围 :实例成员可访问其他实例成员,也可访问静态成员。 class Employee { // 实例字段:每个员工有自己的姓名和工号 public string Name; public int Id; // 实例方法:访问实例字段和静态字段 public void ShowInfo () { Console.WriteLine( $"姓名: {Name} ,工号: {Id} ,部门: {Department} " ); } // 静态字段:所有员工共享的部门信息 public static string Department = "研发部" ; } // 使用实例成员 Employee emp1 = new Employee(); emp1.Name = "张三" ; emp1.Id = 1001 ; emp1.ShowInfo(); // 输出:姓名:张三,工号:1001,部门:研发部 Employee emp2 = new Employee(); emp2.Name = "李四" ; emp2.Id = 1002 ; emp2.ShowInfo(); // 输出:姓名:李四,工号:1002,部门:研发部
2. 静态字段 • 归属 :属于 类本身 ,所有实例共享同一个值,内存中仅一份副本。 // 延续上面的Employee类 Employee.Department = "产品部" ; // 修改静态字段 emp1.ShowInfo(); // 输出:姓名:张三,工号:1001,部门:产品部(emp1的部门也变了) emp2.ShowInfo(); // 输出:姓名:李四,工号:1002,部门:产品部(emp2的部门也变了)
3. 从类的外部访问静态成员 • 推荐方式 : 类名.静态成员 (语义清晰,明确是类级成员)。 • 不推荐方式 : 实例名.静态成员 (编译器允许,但易误导,看起来像实例成员)。 // 推荐:类名访问静态成员 Console.WriteLine(Employee.Department); // 不推荐:实例名访问静态成员(编译器会警告) Employee emp = new Employee(); Console.WriteLine(emp.Department); // 本质还是访问Employee.Department
4. 静态函数成员(静态方法) • 核心限制 :静态方法 不能访问实例成员 (因为没有 this 指向具体实例),但可访问静态成员。 • 典型场景 :工具类(如数学计算、数据转换)、全局逻辑。 static class MathTool // 静态类:只能包含静态成员,不能实例化 { // 静态字段 public static readonly double PI = 3.1415926 ; // 静态方法:计算圆面积(仅访问静态成员) public static double CalculateCircleArea ( double radius ) { return PI * radius * radius; } // 错误:静态方法不能访问实例成员(静态类中也无法定义实例成员) // public string Version = "1.0"; } // 使用静态方法(无需new) double area = MathTool.CalculateCircleArea( 5 ); Console.WriteLine(area); // 输出:78.539815
5. 其他静态类成员类型 除了静态字段 / 方法,还有这些常用静态成员:
表格
public static string AppName { get; set; } static MathTool() { PI = 3.14; } public const int MaxValue = 100;
四、成员常量 vs 静态量:深度对比 新手极易混淆 const (常量)、 static (静态字段)、 static readonly (静态只读字段),以下是精准区分:
1. 成员常量(const) • 本质 :编译期确定值, 隐式静态 (无需加 static ,加了会报错)。 • 限制 :仅支持基本类型(int、string、double 等),必须声明时赋值,不可修改。 • 存储 :值直接嵌入 IL 代码,而非存储在类的静态区域。 class Config { // 正确:常量声明 public const int Timeout = 30 ; // 错误:const不能加static // public static const string Url = "https://example.com"; // 错误:const不能后期赋值 // public const double PI; // static Config() { PI = 3.14; } }
2. 常量 vs 静态量 vs 静态只读量(核心对比) 表格
class Example { // 常量:编译期固定 public const int MaxRetry = 3 ; // 静态字段:可动态修改 public static string AppVersion = "1.0.0" ; // 静态只读:运行期初始化,不可修改 public static readonly DateTime AppStartDate; // 静态构造函数:初始化静态只读字段 static Example () { AppStartDate = DateTime.Now; // 运行期赋值 } } // 测试 Example.AppVersion = "1.0.1" ; // 静态字段可改 // Example.AppStartDate = DateTime.Now; // 报错:只读字段不能赋值 // Example.MaxRetry = 5; // 报错:常量不能赋值
五、属性(Property):字段的 “安全封装” 属性是对字段的封装,解决了 “字段直接暴露导致数据不可控” 的问题,分为三类:
1. 自动属性(简化版) 编译器自动生成私有字段(幕后字段),无需手动定义,适合简单场景。
class User { // 自动属性:读+写 public string Username { get ; set ; } // 自动只读属性:仅能通过构造函数赋值 public string UserId { get ; } // 自动属性:只读(私有写) public int Age { get ; private set ; } // 构造函数初始化只读属性 public User ( string userId ) { UserId = userId; Age = 18 ; // 私有写可在类内部赋值 } }
2. 手动属性(自定义逻辑) 自定义 get / set 逻辑,支持数据验证、日志记录等,是属性的核心用法。
class Product { // 私有字段:底层存储 private decimal _price; // 手动属性:封装_price,添加验证逻辑 public decimal Price { get { Console.WriteLine( "读取价格" ); return _price; } set { if ( value < 0 ) throw new ArgumentException( "价格不能为负数" ); Console.WriteLine( "设置价格:" + value ); _price = value ; } } } // 使用属性 Product p = new Product(); p.Price = 99.9 m; // 调用set,输出:设置价格:99.9 Console.WriteLine(p.Price); // 调用get,输出:读取价格 → 99.9 // p.Price = -10; // 抛出异常:价格不能为负数
3. 属性的核心优势 • 封装性:隐藏字段的存储细节,仅暴露可控的访问接口。 • 扩展性:后续可在 get / set 中添加日志、通知等逻辑,不影响外部调用。 六、构造函数:初始化的核心 构造函数是类中用于初始化对象 / 类的特殊方法,分为 实例构造函数 和 静态构造函数 ,两者差异是核心考点:
1. 实例构造函数 • 语法 :与类名同名,无返回值,可加访问修饰符(public/private 等)。 • 执行时机 :每次 new 创建实例时执行(创建几个实例,执行几次)。 • 重载 :支持多个构造函数(参数不同),可通过 this() 调用其他构造函数。 class Car { public string Brand; public int Year; // 无参构造函数 public Car () { Brand = "未知" ; Year = DateTime.Now.Year; Console.WriteLine( "无参构造执行" ); } // 有参构造函数(重载) public Car ( string brand ) : this () // 调用无参构造 { Brand = brand; Console.WriteLine( "有参构造执行" ); } // 多参数构造函数(重载) public Car ( string brand, int year ) : this ( brand ) // 调用单参数构造 { Year = year; Console.WriteLine( "多参数构造执行" ); } } // 测试:构造函数链式调用 Car c = new Car( "宝马" , 2024 ); /* 输出顺序: 无参构造执行 有参构造执行 多参数构造执行 */
2. 静态构造函数 • 语法 : static 类名() ,无参数、无访问修饰符、不可重载。 • 执行时机 :第一次访问类的静态成员 / 创建实例时, 仅执行一次 (CLR 自动调用,无法手动触发)。 • 核心用途 :初始化静态成员(如加载配置、注册服务)。 class LogManager { // 静态字段 public static string LogPath; // 静态构造函数(仅执行一次) static LogManager () { // 初始化静态字段(模拟从配置读取) LogPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "log.txt" ); Console.WriteLine( "静态构造函数执行:初始化日志路径" ); } // 静态方法 public static void WriteLog ( string message ) { File.AppendAllText(LogPath, $" {DateTime.Now} : {message} \n" ); } } // 第一次调用静态方法,触发静态构造函数 LogManager.WriteLog( "程序启动" ); // 第二次调用,静态构造函数不再执行 LogManager.WriteLog( "操作完成" );
3. 对象初始化语句 简化对象创建,无需定义多个构造函数,直接为属性 / 字段赋值:
class Book { public string Title { get ; set ; } public string Author { get ; set ; } public int Pages { get ; set ; } } // 对象初始化语句(无需构造函数) Book book = new Book { Title = "C#实战 " , Author = "李四" , Pages = 450 };
七、析构函数:对象的 “清理者” • 语法 : ~类名() ,无参数、无访问修饰符、不可重载。 • 执行时机 :GC(垃圾回收器)回收对象时执行, 不可手动调用 ,执行时机不可预测。 • 核心用途 :释放非托管资源(如文件句柄、数据库连接、网络套接字)。 class FileReader { // 非托管资源:文件流 private FileStream _stream; public FileReader ( string path ) { _stream = new FileStream(path, FileMode.Open); } // 析构函数:释放非托管资源 ~FileReader() { // 安全释放资源(null判断) _stream?.Close(); _stream?.Dispose(); Console.WriteLine( "析构函数执行:文件流已释放" ); } } // 测试(析构函数由GC自动调用) FileReader reader = new FileReader( "test.txt" ); reader = null ; // 置为null,让GC回收 GC.Collect(); // 手动触发GC(仅测试用,实际不推荐)
注意:C# 中推荐使用 IDisposable 接口替代析构函数,可手动控制资源释放,更灵活。
八、readonly 修饰符:运行期只读 • 核心特性 : readonly 字段 仅能在声明时或构造函数中赋值 ,后续不可修改。 • 实例 readonly:在实例构造函数中赋值,每个实例有独立值。 • 静态 readonly:在静态构造函数中赋值,类级全局值。 class Order { // 实例readonly:每个订单的ID唯一 public readonly string OrderId; // 静态readonly:所有订单的默认币种 public static readonly string DefaultCurrency; // 实例构造函数:赋值实例readonly public Order () { OrderId = Guid.NewGuid().ToString(); // 运行期生成唯一ID } // 静态构造函数:赋值静态readonly static Order () { DefaultCurrency = "CNY" ; // 运行期初始化 } } Order order1 = new Order(); Order order2 = new Order(); Console.WriteLine(order1.OrderId); // 唯一ID Console.WriteLine(order2.OrderId); // 另一唯一ID // order1.OrderId = "123"; // 报错:readonly字段不能赋值 // Order.DefaultCurrency = "USD"; // 报错:静态readonly字段不能赋值
九、this 关键字:当前实例的 “代名词” this 代表 当前对象实例 ,主要有 4 个核心用途:
1. 区分字段与参数(同名时) class Person { private string _name; public Person ( string name ) { this ._name = name; // this._name是字段,name是参数 } }
2. 调用同类的其他构造函数 class Person { public string Name { get ; set ; } public int Age { get ; set ; } public Person () : this ( "未知" , 18 ) // 调用双参数构造 { } public Person ( string name, int age ) { this .Name = name; this .Age = age; } }
3. 作为参数传递给其他方法 class Person { public void PrintInfo () { ShowDetails( this ); // 将当前实例传递给方法 } private void ShowDetails ( Person p ) { Console.WriteLine( $"姓名: {p.Name} ,年龄: {p.Age} " ); } }
4. 在索引器中使用(代表当前对象) class Collection { private string [] _items = new string [ 10 ]; // 索引器:this代表当前对象 public string this [ int index] { get { return _items[index]; } set { _items[index] = value ; } } }
十、索引器:对象的 “数组式访问” 索引器允许对象像数组一样通过 [下标] 访问成员,核心语法:
public 类型 this[参数列表] { get; set; }
1. 基础索引器(int 下标) class StudentList { private List< string > _students = new List< string >(); // 索引器:int下标访问学生姓名 public string this [ int index] { get { if (index < 0 || index >= _students.Count) throw new IndexOutOfRangeException( "索引超出范围" ); return _students[index]; } set { if (index < 0 || index >= _students.Count) _students.Add( value ); else _students[index] = value ; } } // 获取学生数量 public int Count => _students.Count; } // 使用索引器 StudentList list = new StudentList(); list[ 0 ] = "张三" ; // 调用set(添加) list[ 1 ] = "李四" ; // 调用set(添加) Console.WriteLine(list[ 0 ]); // 调用get → 张三 Console.WriteLine(list.Count); // 输出:2
2. 重载索引器(多类型下标) class StudentList { // 重载索引器:string下标(根据姓名查索引) public int this [ string name] { get { return _students.IndexOf(name); } } } // 使用重载索引器 Console.WriteLine(list[ "李四" ]); // 输出:1
总结(核心关键点) 1. 类的成员核心分类 :按 “归属” 分为实例成员(属于对象,每次 new 都初始化)和静态成员(属于类,仅初始化一次);按 “功能” 分为字段、方法、属性、构造函数等。 • const 是编译期常量(不可改、仅基本类型), static readonly 是运行期只读常量(可任意类型); • 实例构造函数初始化对象(每次 new 执行),静态构造函数初始化类(仅执行一次)。 • this 代表当前实例,索引器让对象支持数组式访问; • readonly 限制字段仅构造函数赋值,保证数据不可篡改。 掌握这些核心知识点,你就能彻底理解 C# 类的设计逻辑和使用方式,后续学习继承、多态、接口等进阶内容也会更轻松。
该文章在 2026/2/9 12:02:58 编辑过