C#学习笔记 -- 类继承、屏蔽与覆写基类方法
1、类继承
通过继承可以定义一个新类, 新类纳入一个已经声明的类并进行扩展
-
可以使用一个已经存在的类作为新类的基础, 已存在的类称为基类(base class), 新类为派生类(derived class), 派生类成员的组成如下
-
本身声明中的成员
-
基类的成员
-
-
要声明一个派生类, 需要在类名后写基类规格说明
-
基类规格说明由冒号和用作基类的类名名称组成
-
派生类直接继承自列出的基类
-
-
派生类扩展他的基类, 因为他包含了基类的成员, 还有他本身声明中的新增功能
-
派生类不能删除他所继承的任何成员
class DerivedClass : BaseClass { ... } class 派生类 : 基类 { }
2、访问继承的成员
继承的成员可以被访问, 就像他们是派生类自己声明的一样(继承的构造函数有些不同)
class Program802Base { public string FieldBase = "基类字段"; public void MethodBase(string value) { Console.WriteLine($"基类方法, 输出{ value }"); } }
class Program802Derived : Program802Base { public string FieldDerived = "派生类字段"; public void MethodDerived(string value) { Console.WriteLine($"派生类方法, 输出{ value }"); } }
static void Main(string[] args) { Program802Derived derived = new Program802Derived(); derived.MethodBase(derived.FieldBase); derived.MethodBase(derived.FieldDerived); derived.MethodDerived(derived.FieldBase); derived.MethodDerived(derived.FieldDerived); }
3、所有类都派生自object类
-
除了object类, 别的类都是派生类, 因为object类是继承层次结构的基础
-
没有基类规格说明隐式派生于object
注意
-
一个类声明的基类规格说明只能有一个单独的类, 这称为单继承
-
虽然只能直接继承一个类, 但派生的层次没有限制, 作为基类的类可以派生自另一个类, 另一个类可能派生于别的类, 直至最终达到object
4、屏蔽基类的成员
虽然派生类不能删除他继承的任何成员, 可以使用于基类名称相同的成员来屏蔽基类成员
-
使用new关键字, 并且以同样的名称屏蔽基类成员
class BaseClass { public string Field; }
class DerivedClass { new public string Field; //使用new关键字, 并且以同样的名称屏蔽基类成员 }
注意
-
要屏蔽一个继承的数据成员, 需要声明一个新的相同类型的成员, 并使用相同的名称
-
通过在派生类中声明新的带有相同签名的函数成员, 可以屏蔽继承的函数成员, 签名不包括返回类型
-
要让编译器知道你在故意的屏蔽继承的成员, 可以使用new修饰符, 否则程序编译成功, 编译器警告你隐藏类一个继承的类
-
也可以屏蔽静态成员
例子
class Program804Base { public string Field = "基类字段"; public void Method(string value) { Console.WriteLine($"基类方法, 输出{ value }"); } }
class Program804Derived : Program804Base { //使用new关键字屏蔽基类字段 new public string Field = "派生类字段, 屏蔽基类同签名方法"; //使用new关键字屏蔽基类方法 new public void Method(string value) { Console.WriteLine($"派生类方法, 屏蔽基类同签名方法, 输出{ value }"); } }
static void Main(string[] args) { Program804Derived program804Derived = new Program804Derived(); program804Derived.Method(program804Derived.Field); }
5、基类访问
如果派生类必须访问被隐藏的继承成员, 在派生类中, 可以使用基类访问表达式
base.Method(); base.Field;
例子
class Program805Base { public string Field = "基类字段"; public void Method(string value) { Console.WriteLine($"基类方法, 输出{ value }"); } }
class Program805Derived : Program804Base { //使用new关键字屏蔽基类字段 new public string Field = "派生类字段, 屏蔽基类同签名方法"; //使用new关键字屏蔽基类方法 new public void Method(string value) { Console.WriteLine($"派生类方法, 屏蔽基类同签名方法, 输出{ value }"); } //基类访问 public void UsingBaseExample() { base.Method(base.Field); } }
static void Main(string[] args) { Program805Derived program805Derived = new Program805Derived(); program805Derived.UsingBaseExample(); }
6、基类的引用
派生类的实例由基类的实例和派生类新增成员组成, 派生类的引用指向整个对象, 包括基类部分
-
如果有一个派生类对象的引用, 就可以获取该对象基类部分的引用
MyDerivedClass derived = new MyDerivedClass(); MyBaseClass mybc = (MyBaseClass) derived;//使用类型转换运算符, 把该引用转换为基类类型将派生类 -
对象强制转换为基类对象的作用是产生的变量只能访问基类的成员(在被覆盖的方法中除外)
例子
class Program806Base { public void Print() { Console.WriteLine("基类方法"); } }
class Program806Derived : Program806Base { public int var1; new public void Print() { Console.WriteLine("派生类方法"); } }
static void Main(string[] args) { Program806Derived derived = new Program806Derived(); Program806Base mybc = (Program806Base) derived; mybc.Print();//基类方法 }
(1)虚方法和覆写方法
虚方法可以将基类的引用访问"升至"派生类中
可以使用基类调用派生类的方法, 只需满足下列条件
-
派生类的方法和基类的方法有相同的签名和返回类型
-
基类的方法使用virtual标注
-
派生类的方法使用override标注
class MyBaseClass { virtual public void Print() }
class MyDerivedClass { override public void Print() }
注意: 与使用new关键字修饰的区别
-
当使用基类引用调用方法时候, 方法调用被传递到派生类并执行, 原因如下
-
基类的方法被标记为virtual
-
在派生类中有匹配的override方法
-
例子
class Base806_1 { virtual public void Print() { Console.WriteLine("基类方法"); } }
class Derived806_1 : Base806_1 { public override void Print() { Console.WriteLine("派生类方法"); } }
static void Main(string[] args) { Derived806_1 derived = new Derived806_1(); Base806_1 mybc = (Base806_1)derived; derived.Print(); //派生类方法 mybc.Print(); //派生类方法 }
注意
-
覆写和被覆写的方法必须有相同的可访问性
-
不能覆写静态方法或者非虚方法
-
方法、属性、索引器、事件都可以被声明为virtual和override
(2)覆写标记为override的方法
覆写方法可以在继承的任何层次出现
-
当使用对象基类部分的引用调用一个被覆写的方法时, 方法的调用被沿派生层次上追溯执行, 一直到标记为override的方法的最高派生(most-derived)版本
-
如果在更高的派生级别有该方法的其他声明, 但没有被标记为override, 那么他们不会被调用
探讨例子
//基类 class MybaseClass { virtual public void Print() { Console.WriteLine("基类方法"); } }
//派生类 class MyDerivedClass : MybaseClass { override public void Print() { Console.WriteLine("派生类方法"); } }
1)使用override声明顶级派生类的Print方法
class SecondDerived : MyDerivedClass { override public void Print() { Console.WriteLine("顶级派生类方法"); } }
会覆写方法的两个低派生级别版本, 如果一个基类的引用被用于调用Print, 就会一直向上传递直到顶级派生类SecondDerived
static void Main(string[] args) { SecondDerived derived = new SecondDerived(); MyBaseClass mybc = (MyBaseClass) derived; derived.Print(); //顶级派生方法 mybc.Print(); //顶级派生方法 }
结论
无论是通过派生类还是基类调用, 都会调用最高派生类中的方法,
通过基类调用时, 调用沿着继承层次向上传递.
2)使用new声明顶级派生类的Print方法
class SecondDerived : MyDerivedClass { //使用new声明顶级派生类的Print方法 new public void Print() { Console.WriteLine("顶级派生类方法"); } }
通过mybc调用Print方法时, 方法调用只向上传递了一级, 到达派生类, 在那里被执行, 隐藏覆写的方法
static void Main(string[] args) { SecondDerived derived = new SecondDerived(); MyBaseClass mybc = (MyBaseClass)derived; derived.Print(); //顶级派生方法 mybc.Print(); //派生类方法 }
(3)覆盖其他成员
virtual/override可以覆盖方法、属性、事件、索引器
例子: 覆盖只读属性
class Base806_3 { public int _myProperty = 5; virtual public int MyProperty { get { return _myProperty; } } }
class Derived806_3 : Base806_3 { //后备字段如果不屏蔽, 编译器会报错, 执行是没有问题的 new private int _myProperty = 10; override public int MyProperty { get { return _myProperty; } } }
static void Main(string[] args) { Derived806_3 derived = new Derived806_3(); Base806_3 mybc = derived; Console.WriteLine(derived.MyProperty);//10 Console.WriteLine(mybc.MyProperty);//10 }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了