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
}