C# 面向对象编程
类与对象、构造函数、继承、多态、抽象类与接口、属性与索引器、运算符重载、记录类型(record)
1. 类与对象
1.1 类的定义
public class Person
{
// 字段
private string _name;
private int _age;
// 属性
public string Name
{
get => _name;
set => _name = value ?? throw new ArgumentNullException(nameof(value));
}
public int Age
{
get => _age;
set => _age = value >= 0 ? value : throw new ArgumentOutOfRangeException();
}
// 自动属性
public string Email { get; set; } = string.Empty;
// init 属性(C# 9+,仅初始化时赋值)
public string Address { get; init; } = string.Empty;
// 计算属性
public bool IsAdult => Age >= 18;
// 构造函数
public Person(string name, int age)
{
_name = name;
_age = age;
}
// 方法
public string Greet() => $"你好,我是{Name},今年{Age}岁";
// 方法重载
public string Greet(string greeting) => $"{greeting},我是{Name}";
public override string ToString() => $"{Name} ({Age})";
}
1.2 对象创建与初始化
// 构造函数 + 对象初始化器
var person = new Person("张三", 25)
{
Email = "zhangsan@example.com",
Address = "北京市"
};
// init 属性只能在初始化时设置
// person.Address = "上海"; // 编译错误!
// with 表达式(record 类型专用)
var record1 = new PersonRecord("李四", 30);
var record2 = record1 with { Age = 31 };
// 目标类型 new(C# 9+)
Person p = new("王五", 28);
List<Person> people = [new("赵六", 22), new("钱七", 35)];
1.3 静态成员
public class MathHelper
{
// 静态字段
public static int InstanceCount { get; private set; }
// 静态构造函数(仅执行一次)
static MathHelper()
{
InstanceCount = 0;
}
// 静态方法
public static double CircleArea(double radius) => Math.PI * radius * radius;
// 静态类(不能实例化)
}
public static class StringExtensions
{
public static bool IsNullOrEmpty(this string? str) =>
string.IsNullOrEmpty(str);
}
2. 构造函数
2.1 构造函数类型
public class Product
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
// 无参构造函数
public Product()
{
Name = "未知商品";
Price = 0;
Category = "未分类";
}
// 参数化构造函数
public Product(string name, decimal price)
{
Name = name;
Price = price;
Category = "未分类";
}
// 构造函数链
public Product(string name, decimal price, string category) : this(name, price)
{
Category = category;
}
}
// C# 12 主构造函数
public class Service(ILogger logger, IConfiguration config)
{
public void DoWork()
{
logger.LogInformation("执行工作...");
var value = config["Key"];
}
// 主构造函数参数可变
public void SetLogger(ILogger newLogger) => logger = newLogger;
}
3. 继承
3.1 基本继承
// 基类
public class Animal
{
public string Name { get; set; }
public int Age { get; set; }
public Animal(string name, int age)
{
Name = name;
Age = age;
}
// virtual 允许子类重写
public virtual string Speak() => $"{Name}发出了声音";
// sealed 阻止进一步重写
public sealed string GetInfo() => $"{Name}, {Age}岁";
}
// 派生类
public class Dog : Animal
{
public string Breed { get; set; }
// 调用基类构造函数
public Dog(string name, int age, string breed) : base(name, age)
{
Breed = breed;
}
// override 重写虚方法
public override string Speak() => $"{Name}汪汪叫!";
// 方法隐藏(不推荐,应使用 override)
public new string GetInfo() => $"{Name} ({Breed}), {Age}岁";
}
3.2 多态
// 运行时多态
Animal animal = new Dog("旺财", 3, "金毛");
Console.WriteLine(animal.Speak()); // "旺财汪汪叫!"
// 多态集合
List<Animal> animals =
[
new Dog("旺财", 3, "金毛"),
new Cat("咪咪", 2, "橘猫"),
new Bird("小鸟", 1, "鹦鹉")
];
foreach (var a in animals)
{
Console.WriteLine(a.Speak()); // 每个对象调用各自实现
}
// is 模式匹配
foreach (var a in animals)
{
switch (a)
{
case Dog d: Console.WriteLine($"狗: {d.Breed}"); break;
case Cat c: Console.WriteLine($"猫: {c.Color}"); break;
case Bird b: Console.WriteLine($"鸟: {b.Species}"); break;
}
}
4. 抽象类与接口
4.1 抽象类
public abstract class Shape
{
public string Color { get; set; }
protected Shape(string color) => Color = color;
// 抽象方法:子类必须实现
public abstract double Area();
public abstract double Perimeter();
// 虚方法:子类可选重写
public virtual string Describe() =>
$"{GetType().Name} (颜色: {Color}, 面积: {Area():F2})";
}
public class Circle : Shape
{
public double Radius { get; }
public Circle(double radius, string color) : base(color) => Radius = radius;
public override double Area() => Math.PI * Radius * Radius;
public override double Perimeter() => 2 * Math.PI * Radius;
}
4.2 接口
// 接口定义契约
public interface IReadable
{
string Read();
}
public interface IWritable
{
void Write(string content);
}
// 接口默认实现(C# 8+)
public interface ILogger
{
void Log(string message);
// 默认实现
void LogWarning(string message) => Log($"[WARN] {message}");
void LogError(string message) => Log($"[ERROR] {message}");
}
// 多接口实现
public class FileStorage : IReadable, IWritable, ILogger
{
private readonly string _path;
public FileStorage(string path) => _path = path;
public string Read() => File.ReadAllText(_path);
public void Write(string content) => File.WriteAllText(_path, content);
public void Log(string message) => Console.WriteLine(message);
}
// C# 13 扩展类型(预览)
// 允许向现有类型添加方法而不修改原始定义
4.3 抽象类 vs 接口
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 多继承 | 单继承 | 多实现 |
| 构造函数 | 有 | 无 |
| 字段 | 可以有 | 仅静态常量 |
| 方法实现 | 可以有具体实现 | C# 8+ 可有默认实现 |
| 访问修饰符 | 任意 | 默认 public |
| 版本控制 | 添加方法不破坏子类 | 添加方法可能破坏实现类 |
| 适用场景 | ”是什么”(IS-A) | “能做什么”(CAN-DO) |
5. 属性与索引器
5.1 属性进阶
public class Temperature
{
private double _celsius;
public double Celsius
{
get => _celsius;
set => _celsius = value;
}
// 派生属性
public double Fahrenheit
{
get => _celsius * 9 / 5 + 32;
set => _celsius = (value - 32) * 5 / 9;
}
// required 属性(C# 11)
public required string Unit { get; init; }
}
// 接口属性
public interface INamed
{
string Name { get; } // 只读
string? Description { get; set; } // 可读写
}
5.2 索引器
public class Matrix
{
private readonly double[,] _data;
public int Rows => _data.GetLength(0);
public int Cols => _data.GetLength(1);
public Matrix(int rows, int cols)
{
_data = new double[rows, cols];
}
// 索引器
public double this[int row, int col]
{
get => _data[row, col];
set => _data[row, col] = value;
}
}
var matrix = new Matrix(3, 3);
matrix[0, 0] = 1.0;
matrix[1, 1] = 2.0;
var value = matrix[2, 2];
6. 运算符重载
public class Vector
{
public double X { get; }
public double Y { get; }
public Vector(double x, double y) => (X, Y) = (x, y);
// 二元运算符
public static Vector operator +(Vector a, Vector b) =>
new(a.X + b.X, a.Y + b.Y);
public static Vector operator -(Vector a, Vector b) =>
new(a.X - b.X, a.Y - b.Y);
// 一元运算符
public static Vector operator -(Vector v) =>
new(-v.X, -v.Y);
// 标量乘法
public static Vector operator *(Vector v, double scalar) =>
new(v.X * scalar, v.Y * scalar);
public static Vector operator *(double scalar, Vector v) => v * scalar;
// 比较运算符(需成对重载)
public static bool operator ==(Vector a, Vector b) =>
a.X == b.X && a.Y == b.Y;
public static bool operator !=(Vector a, Vector b) => !(a == b);
// 必须重写 Equals 和 GetHashCode
public override bool Equals(object? obj) =>
obj is Vector v && this == v;
public override int GetHashCode() => HashCode.Combine(X, Y);
// 隐式/显式转换
public static implicit operator Vector((double x, double y) tuple) =>
new(tuple.x, tuple.y);
public override string ToString() => $"({X}, {Y})";
}
// 使用
var v1 = new Vector(1, 2);
var v2 = new Vector(3, 4);
var v3 = v1 + v2; // (4, 6)
var v4 = v1 * 2.0; // (2, 4)
Vector v5 = (5.0, 6.0); // 元组隐式转换
7. 记录类型 (Record)
Record 是 C# 9 引入的不可变引用类型,专为值语义设计:
7.1 记录类型定义
// 位置记录(最简洁)
public record Person(string Name, int Age);
// 可添加额外成员
public record Student(string Name, int Age, string School) : Person(Name, Age)
{
public double GPA { get; init; }
public string Grade => GPA switch
{
>= 3.7 => "A",
>= 3.0 => "B",
>= 2.0 => "C",
_ => "D"
};
}
// record struct(C# 10,值类型记录)
public record struct Point(double X, double Y)
{
public double Distance => Math.Sqrt(X * X + Y * Y);
}
// readonly record struct
public readonly record struct Money(decimal Amount, string Currency);
7.2 记录类型特性
var p1 = new Person("张三", 25);
var p2 = new Person("张三", 25);
// 值相等性(与 class 不同!)
Console.WriteLine(p1 == p2); // True(class 为 False)
Console.WriteLine(p1.Equals(p2)); // True
// with 表达式(非破坏性修改)
var p3 = p1 with { Age = 26 };
Console.WriteLine(p3); // Person { Name = 张三, Age = 26 }
// 解构
var (name, age) = p1;
Console.WriteLine($"{name}, {age}"); // 张三, 25
// ToString 自动生成
Console.WriteLine(p1); // Person { Name = 张三, Age = 25 }
7.3 Record vs Class 选择
| 场景 | 推荐类型 | 原因 |
|---|---|---|
| DTO / 数据传输 | record | 值相等、不可变、with 表达式 |
| 值对象 | record 或 readonly record struct | 值语义 |
| 领域实体 | class | 需要可变状态和引用相等 |
| 配置对象 | record | 不可变,易于克隆 |
| 性能敏感的小对象 | readonly record struct | 避免堆分配 |