C# 类(Class)(手把手讲解)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战(已更新的所有项目都能学习) / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新开坑项目:《Spring AI 项目实战》 正在持续爆肝中,基于 Spring AI + Spring Boot 3.x + JDK 21..., 点击查看 ;
- 《从零手撸:仿小红书(微服务架构)》 已完结,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
在 C# 开发中,类(Class) 是面向对象编程(OOP)的核心概念之一。它不仅是代码组织的基本单元,也是实现复用性、封装性和扩展性的关键工具。无论是构建简单的控制台应用,还是复杂的分布式系统,开发者都离不开对 C# 类(Class) 的理解和应用。本文将从基础到进阶,通过案例与代码示例,深入讲解 C# 类(Class) 的设计原理与实践技巧,帮助读者掌握这一重要概念。
类的定义与基本结构
什么是类?
类可以理解为一个“蓝图”或“模板”,用于描述某一类型对象的共同特征和行为。例如,一个 Person
类可以定义人类的属性(如姓名、年龄)和方法(如说话、行走)。通过这个模板,我们可以创建多个 Person
对象,每个对象都拥有类定义的属性和行为。
示例代码:定义一个简单的类
public class Person
{
// 字段(Field):存储数据
private string name;
private int age;
// 方法(Method):定义行为
public void SayHello()
{
Console.WriteLine($"Hello, my name is {name}!");
}
}
类的成员类型
一个类通常包含以下成员:
- 字段(Field):用于存储数据,通常声明为
private
以确保封装性。 - 属性(Property):用于提供对字段的访问控制,例如通过
get
和set
方法。 - 方法(Method):定义类的行为,如计算、操作数据等。
- 构造函数(Constructor):用于初始化对象的状态。
表格对比:字段与属性的区别
(空一行后插入表格)
| 特性 | 字段(Field) | 属性(Property) |
|--------------|----------------------------|-------------------------------|
| 访问控制 | 通常直接暴露(如 public
) | 可通过 get
/set
限制访问 |
| 代码简洁性 | 更简单,直接赋值 | 需定义 get
和 set
方法 |
| 功能扩展 | 无法直接添加逻辑 | 可在 get
/set
中添加验证 |
类的实例化与对象
对象的创建
通过 new
关键字可以创建类的实例(对象)。例如:
Person person = new Person();
person.name = "Alice"; // 直接访问字段(但需确保字段是 `public`)
person.SayHello(); // 调用方法
注意事项:字段的访问权限
如果字段声明为 private
,则外部无法直接访问。此时需要通过属性或方法间接操作:
public class Person
{
private string name;
// 通过属性提供安全的访问
public string Name
{
get { return name; }
set { name = value; }
}
}
// 使用属性
Person person = new Person();
person.Name = "Alice"; // 通过属性设置值
类的访问修饰符
什么是访问修饰符?
访问修饰符决定了类、成员或方法的可见范围。C# 中常见的修饰符包括 public
、private
、protected
等。
示例:不同修饰符的效果
public class AccessExample
{
private int privateField; // 仅当前类可访问
protected int protectedField; // 当前类及子类可访问
public int publicField; // 全局可访问
}
表格:修饰符的作用域对比
(空一行后插入表格)
| 修饰符 | 作用域 |
|----------|-----------------------------------------------------------------------|
| public
| 可被任何其他代码访问。 |
| private
| 仅限于声明该成员的类内部访问。 |
| protected
| 可被同一类或其派生类访问。 |
| internal
| 可被同一程序集(Assembly)内的代码访问。 |
继承:类的扩展与复用
继承的概念
继承允许一个类(子类)继承另一个类(父类)的属性和方法,从而实现代码复用。例如,Student
类可以继承自 Person
类:
public class Student : Person
{
public string StudentID { get; set; }
// 重写父类方法
public override void SayHello()
{
base.SayHello(); // 调用父类方法
Console.WriteLine($"My student ID is {StudentID}");
}
}
继承的注意事项
- 基类与派生类:子类可以添加新成员,但无法移除继承的成员。
- 构造函数:子类需显式调用父类的构造函数(通过
base()
)。
多态:灵活的行为定义
多态的实现方式
多态允许不同类的对象通过统一接口调用,但表现出不同行为。C# 中通过 方法重写(Override) 和 接口实现 实现多态:
示例:通过虚方法实现多态
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("Animal makes a sound");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Dog barks!");
}
}
// 使用
Animal animal = new Dog();
animal.MakeSound(); // 输出 "Dog barks!"
关键点:virtual
和 override
virtual
:在父类方法中声明,允许子类重写。override
:在子类中重写父类的virtual
方法。
封装:隐藏复杂性与保护数据
封装的核心思想
封装通过将数据和行为捆绑,并限制直接访问内部状态,从而提升代码的安全性和可维护性。例如:
public class BankAccount
{
private decimal balance; // 私有字段,外部无法直接修改
public decimal Balance
{
get { return balance; }
private set // 仅允许类内部设置值
{
if (value < 0) throw new ArgumentException("Balance cannot be negative");
balance = value;
}
}
public void Deposit(decimal amount)
{
Balance += amount; // 通过属性验证
}
}
封装的优势
- 数据验证:确保数据符合业务规则(如金额不能为负)。
- 接口稳定:即使内部实现变化,外部调用方式不变。
高级特性:抽象类与接口
抽象类(Abstract Class)
抽象类不能被实例化,主要用于定义通用功能和强制子类实现特定方法:
public abstract class Shape
{
public abstract double CalculateArea(); // 抽象方法,无实现
// 具体方法
public void PrintArea()
{
Console.WriteLine($"Area: {CalculateArea()}");
}
}
public class Circle : Shape
{
public double Radius { get; set; }
public override double CalculateArea()
{
return Math.PI * Radius * Radius;
}
}
接口(Interface)
接口定义了一组方法“契约”,类可以实现多个接口,实现多重继承:
public interface IAnimal
{
void Move();
}
public interface IEat
{
void Eat();
}
public class Dog : IAnimal, IEat
{
public void Move() => Console.WriteLine("Dog is running");
public void Eat() => Console.WriteLine("Dog is eating");
}
实际案例:学生管理系统
需求:管理学生信息
我们需要一个系统,记录学生的姓名、年龄、成绩,并支持计算平均分。
步骤 1:定义 Student
类
public class Student
{
public string Name { get; set; }
public int Age { get; set; }
public List<int> Scores { get; set; } = new List<int>();
public double CalculateAverage()
{
return Scores.Average();
}
}
步骤 2:创建 StudentManager
类
public class StudentManager
{
private List<Student> students = new List<Student>();
public void AddStudent(Student student)
{
students.Add(student);
}
public void PrintAllAverages()
{
foreach (var student in students)
{
Console.WriteLine($"{student.Name}'s average: {student.CalculateAverage()}");
}
}
}
步骤 3:使用案例
var manager = new StudentManager();
var alice = new Student { Name = "Alice", Age = 20 };
alice.Scores.Add(90); alice.Scores.Add(85);
manager.AddStudent(alice);
manager.PrintAllAverages(); // 输出 Alice 的平均分
常见问题与最佳实践
问题 1:为什么类成员需要封装?
直接暴露字段可能导致数据被意外修改(如 student.Age = -5
)。通过属性,可以添加验证逻辑,确保数据合法性。
问题 2:抽象类与接口的区别?
- 抽象类:适合定义共享实现和强制继承结构。
- 接口:适合定义行为“契约”,支持多继承。
最佳实践
- 单一职责原则:一个类只负责一个功能模块。
- 避免过度继承:优先使用组合而非继承扩展功能。
- 合理使用
private
:默认将字段设为private
,通过属性提供访问。
结论
C# 类(Class) 是构建复杂应用的基础,它通过封装、继承和多态等特性,帮助开发者编写高效、可维护的代码。无论是设计简单的数据结构,还是构建庞大的系统,理解类的定义、成员管理、继承关系以及高级特性(如抽象类和接口)都是关键。通过实际案例的演练,读者可以进一步掌握如何将理论应用于实践,最终实现灵活且健壮的 C# 应用程序。
希望本文能为编程初学者和中级开发者提供清晰的指导,并激发对面向对象编程更深层次的兴趣。