Skip to content

C# - 类

C# - 类(Classes)和对象(Objects)

Section titled “C# - 类(Classes)和对象(Objects)”

类是 C# 中面向对象编程(OOP)的基石。一个类是创建对象的蓝图。它定义了一组属性(数据)和方法(行为),这些属性和方法将由该类的对象拥有。一个对象是类的一个实例,是从蓝图创建的具体实体。

可以把 Car 类看作蓝图。它定义了所有汽车都具有 Color、Make 和 Model 等属性,以及 StartEngine() 和 Accelerate() 等方法。一辆真实的汽车,比如一辆“红色的 Tesla Model S”,就是 Car 类的一个对象(一个实例)。

类使用 class 关键字定义。现代 C# 使用属性(properties)来公开数据,它提供了一种简洁的语法来获取和设置值,同时能够在幕后实现逻辑。

public class BankAccount
{
// 属性:可公开访问的数据成员
public string AccountNumber { get; set; }
public string Owner { get; set; }
// 带有私有设置器(private setter)的属性,用于控制修改
public decimal Balance { get; private set; }
// 构造函数:用于创建和初始化对象的特殊方法
public BankAccount(string owner, decimal initialBalance)
{
Owner = owner;
Balance = initialBalance;
AccountNumber = Guid.NewGuid().ToString(); // 生成一个唯一的账号
}
// 方法:对象可以执行的操作
public void Deposit(decimal amount)
{
if (amount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "Deposit amount must be positive.");
}
Balance += amount;
}
public void Withdraw(decimal amount)
{
if (amount <= 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "Withdrawal amount must be positive.");
}
if (Balance - amount < 0)
{
throw new InvalidOperationException("Insufficient funds.");
}
Balance -= amount;
}
}

你使用 new 关键字创建对象(实例化一个类)。一旦你拥有了一个对象,你就可以使用点号(.)运算符来访问其公共成员。

using System;
// 创建 BankAccount 类的实例
var myAccount = new BankAccount("Alice Smith", 1000.00m);
Console.WriteLine($"Account created for {myAccount.Owner}.");
Console.WriteLine($"Account Number: {myAccount.AccountNumber}");
Console.WriteLine($"Initial Balance: {myAccount.Balance:C}");
// 使用对象的方法
myAccount.Deposit(250.50m);
Console.WriteLine($"New balance after deposit: {myAccount.Balance:C}");
try
{
myAccount.Withdraw(500.00m);
Console.WriteLine($"New balance after withdrawal: {myAccount.Balance:C}");
myAccount.Withdraw(2000.00m); // 这将抛出异常
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
Account created for Alice Smith.
Account Number: [这里会显示一个唯一的 GUID]
Initial Balance: $1,000.00
New balance after deposit: $1,250.50
New balance after withdrawal: $750.50
Error: Insufficient funds.

封装(Encapsulation)和访问修饰符(Access Modifiers)

Section titled “封装(Encapsulation)和访问修饰符(Access Modifiers)”

封装(Encapsulation),或数据隐藏,是面向对象编程的核心原则。它意味着将数据(属性)和操作该数据的方法捆绑在一个单元(类)中,并控制从外部对该数据的访问。这可以防止意外损坏并保持对象的内部状态有效。

访问修饰符控制类及其成员的可见性:

  • public:可在任何地方访问。
  • private:仅可在同一类中访问。这是成员的默认设置。
  • protected:可在类内部和派生(子)类中访问。
  • internal:仅可在同一项目(程序集)中访问。这是类的默认设置。
  • protected internal:可在同一项目内访问,或在其他项目中的派生类中访问。
  • private protected:可由派生类访问,但仅限于同一项目内。

在我们的 BankAccount 示例中,Balance 具有 private set。这意味着任何人都可以读取余额,但只有 BankAccount 类内部的代码(例如 Deposit 和 Withdraw 方法)才能更改它。这是封装的一个完美示例。

构造函数(Constructors)和对象初始化

Section titled “构造函数(Constructors)和对象初始化”

构造函数是在创建对象时调用的特殊方法。它的目的是初始化对象的状态。如果你没有定义构造函数,系统会为你提供一个默认的无参数构造函数。

你还可以使用对象初始化器(object initializers)以更具可读性的方式在构造函数运行后设置公共属性。

public class Customer
{
public int Id { get; set; }
public string Name { get; set; } = "Anonymous"; // 默认值
public bool IsPremium { get; set; }
// 无参数构造函数
public Customer() {}
}
// 使用无参数构造函数和对象初始化器
var customer = new Customer
{
Id = 101,
Name = "Bob Johnson",
IsPremium = true
};

一个 static 成员(属性或方法)属于类本身,而不是类的实例。你使用类名而不是对象变量来访问它。静态成员在类的所有实例之间共享。

public class ConfigurationManager
{
// 静态属性 - 整个应用程序中只存在一个副本
public static string ApiUrl { get; set; } = "https://api.example.com";
// 静态方法
public static void LoadConfiguration()
{
Console.WriteLine($"Loading configuration from {ApiUrl}...");
}
}
// 使用类名访问静态成员
ConfigurationManager.LoadConfiguration();
ConfigurationManager.ApiUrl = "https://api-test.example.com";
Console.WriteLine($"API URL set to: {ConfigurationManager.ApiUrl}");

在旧版 C# 中,有时会提到析构函数(destructors,~ClassName())。然而,处理资源清理(如关闭文件、网络连接或数据库句柄)的现代且正确的方法是实现 IDisposable 接口并使用 using 语句。

public class FileReader : IDisposable
{
private StreamReader _reader;
public FileReader(string filePath)
{
_reader = new StreamReader(filePath);
}
public string ReadLine() => _reader.ReadLine();
// Dispose 方法清理资源
public void Dispose()
{
_reader?.Dispose();
Console.WriteLine("File reader disposed.");
}
}
// 'using' 语句确保 Dispose() 自动被调用
// 即使发生错误。
using (var reader = new FileReader("mydata.txt"))
{
// 使用 reader...
}
// 在此点,reader.Dispose() 已经被调用。