单继承
public class Animal
{
public string Name = "";
public void Sleep() => Console.WriteLine($"{Name} sleeping");
}
public class Dog : Animal
{
public void Bark() => Console.WriteLine($"{Name} woof!");
}
var d = new Dog { Name = "Rex" };
d.Sleep(); // 继承自 Animal
d.Bark(); // 自己的
C# 只有单继承——一个类只能有一个基类。但可以实现多个接口(第 11 篇)。
调用父构造
public class Animal
{
public string Name;
public Animal(string name) { Name = name; }
}
public class Dog : Animal
{
public string Breed;
public Dog(string name, string breed) : base(name)
{
Breed = breed;
}
}
: base(...) 在自己构造前调父构造。
virtual + override
public class Animal
{
public virtual void Speak() => Console.WriteLine("...");
}
public class Cat : Animal
{
public override void Speak() => Console.WriteLine("meow");
}
public class Dog : Animal
{
public override void Speak() => Console.WriteLine("woof");
}
Animal a = new Cat();
a.Speak(); // meow(运行时多态——根据实际类型调)
- 父用
virtual标记"可以被子类重写" - 子用
override重写 - 多态生效:声明类型是 Animal,调用走实际类型的实现
不用 virtual = 隐藏父方法
public class Animal
{
public void Speak() => Console.WriteLine("...");
}
public class Cat : Animal
{
public new void Speak() => Console.WriteLine("meow");
}
Animal a = new Cat();
a.Speak(); // "..."(按声明类型,不多态)
Cat c = new Cat();
c.Speak(); // "meow"
new 关键字明确"故意隐藏父方法"——但 99% 时候你想要的是 override。
abstract
public abstract class Shape
{
public abstract double Area(); // 没有实现
public virtual void Describe() => Console.WriteLine($"Area = {Area()}");
}
public class Circle : Shape
{
public double Radius;
public override double Area() => Math.PI * Radius * Radius;
}
// new Shape() ❌ 不能实例化抽象类
var c = new Circle { Radius = 5 };
c.Describe();
abstract class:不能实例化,强制子类提供实现。
abstract method:只签名没体,子类必须 override。
sealed
public sealed class Config { ... } // 类:不能被继承
public class Animal
{
public virtual void Speak() { }
}
public class Cat : Animal
{
public sealed override void Speak() { ... } // 方法:子类不能再 override
}
默认 sealed 是好实践——避免误继承造成的脆弱设计。需要被继承的类显式 unseal。
protected
public class Animal
{
protected int _id; // 子类能访问,外部不能
}
public class Dog : Animal
{
public void X() { _id = 5; } // ✅
}
var a = new Animal();
a._id = 5; // ❌
类型转换
Animal a = new Dog();
// 向下转换
if (a is Dog d) // 模式匹配
{
d.Bark();
}
Dog d2 = (Dog)a; // 显式 cast,失败抛 InvalidCastException
Dog? d3 = a as Dog; // as:失败返 null(只对引用类型 / 可空类型)
is / as 更安全——优先用 if (a is Dog d) ...。
base 调父方法
public class Cat : Animal
{
public override void Speak()
{
base.Speak(); // 先调父
Console.WriteLine("meow");
}
}
object:所有类的根
object o = 42; // int 也是 object(装箱)
o = "hello";
o = new Person();
// object 自带的方法
o.ToString();
o.GetType();
o.GetHashCode();
o.Equals(other);
所有类隐式继承 object。
组合 vs 继承
继承很容易过度使用:
// ❌ 强行继承
public class FlyingDog : Dog { ... } // 然后 SwimmingFlyingDog?
// ✅ 组合 + 接口
public interface ICanFly { void Fly(); }
public interface ICanSwim { void Swim(); }
public class Duck : Animal, ICanFly, ICanSwim { ... }
优先组合 + 接口——继承用在"是一个"非常明确的关系上(Cat 是 Animal),不要用来"借用功能"。
new 在继承层级中的几个意思
public new void Speak() { } // 1. 隐藏父方法(不多态)
new Person() // 2. 创建实例(运算符)
where T : new() // 3. 泛型约束:T 必须有无参构造
不同上下文不同含义。
→ 下一篇 接口