C#继承
C#继承
继承是在类之间建立一种相交的关系,使得新定义的派生类的实例可以继承已有的基类的特征并且还可以添加新的功能。以前对继承的理解仅仅限于定义,下面是我查了些资料、写了点代码的总结。
1.C#继承的特点
(1) 派生类是对基类的扩展,派生类可以添加新的成员,但不能移除已经继承的成员的定义。
(2)继承是可以传递的。如果C从B中派生,B又从A中派生,那么C不仅继承了B中声明的成员,同样也继承了A中声明的成员。
(3)构造函数和析构函数不能被继承,除此之外其他成员能被继承。基类中成员的访问方式只能决定派生类能否访问它们。
(4)派生类如果定义了与继承而来的成员同名的新成员,那么就可以覆盖已继承的成员,但这兵不是删除了这些成员,只是不能再访问这些成员。
(5)类可以定义虚方法、虚属性及虚索引指示器,它的派生类能够重载这些成员,从而使类可以展示出多态性。
(6)派生类只能从一个类中继承,可以通过接口来实现多重继承。
2.一个简单的基础实现
//从运行结果可以看到很重要的一点,继承时先执行父类构造函数, //接着再执行子类构造函数,最后再执行方法 class Program { static void Main(string[] args) { Man man = new Man(); man.Eat(); //在派生类中访问基类中的成员一般有2种方式,一是调用base.<成员> 调用基类的方法,二是显示类型转换为父类 ((People)man).Eat(); } } public class People { public People() { Console.WriteLine("父类的构造函数"); } public void Eat() { Console.WriteLine("父类吃饭"); } } class Man:People { public Man() { Console.WriteLine("子类构造函数"); } public void WhoEat() { base.Eat(); } }
3.隐藏基类成员
//当派生类需要覆盖基类的方法时,C#使用new修饰符来实现隐藏基类成员,不写new也是默认隐藏的,根据规范应该写,编译器会提示 class Program { static void Main(string[] args) { Man man = new Man(); man.Eat(); } } public class People { public People() { Console.WriteLine("父类的构造函数"); } public void Eat() { Console.WriteLine("我是父类"); } } class Man:People { public Man() { Console.WriteLine("子类构造函数"); } public new void Eat() { Console.WriteLine("我是子类"); } }
4.抽象类、密封类、抽象方法和虚方法
首先还有一种类需要注意,那就是密封类。如果我们对类不作任何约束,也就是说所有类都可以被继承,这种继承的滥用会导致类的层次结构十分庞大,类与类之间的关系会变得很乱导致无法理解。因此C#提供了密封类,我们只需在父类前加上sealed修饰符,那这个类将不能被继承了。密封方法也是在方法前加上sealed修饰符。
抽象类和密封类刚好相反,它是为继承而生的。抽象类不能实例化,抽象方法没有具体执行代码,必须在非抽象的派生类中重写。也就是基类并不实现任何执行代码,只是进行定义。这一点和接口有相同的地方。
class Program { static void Main(string[] args) { Man man = new Man(); man.Eat(); man.Say(); } } public abstract class People { //注意:如果类中有抽象方法,则类必须声明为抽象类。 public People() { Console.WriteLine("父类的构造函数"); } public abstract void Eat(); //有时候不想把类声明为抽象类,但又想实现方法在基类里不具体实现, //而是想实现方法由派生类重写。遇到这种情况时可使用virtual关键字将方法声明为虚方法 public virtual void Say() { //注意虚方法必须声明方法主体,抽象方法则不需要 Console.WriteLine("我是父类的虚方法"); } } class Man:People { public Man() { Console.WriteLine("子类构造函数"); } public override void Eat() { Console.WriteLine("我是子类"); } public override void Say() { Console.WriteLine("我是子类的Say方法"); } }
5.有参数的构造函数
class Program { static void Main(string[] args) { Man man = new Man("构造函数"); //结果仍然是先输出父类构造函数,然后再输出子类构造函数 } } public class People { public People(string s) { Console.WriteLine("父类"+s); } } class Man:People { //在继承时,如果基类构造函数是有参数的,子类构造函数也必须有一个有参数的构造函数,否则会报错 public Man(string s):base(s) { Console.WriteLine("子类构造函数"); } }
override是实现的多态的关键!
6、清晰的示例
对于new关键字,无论父类有什么修饰,都表示父类覆盖了子类,相当于父子隔绝。父类的声明是不能访问子类的。
public class A { public void SayHello() { Console.WriteLine("I'm A!"); } public virtual void Identify() { Console.WriteLine("from A"); } } public class B : A { public new void SayHello() { Console.WriteLine("I'm B!"); } public new void Identify() { Console.WriteLine("form B!"); } } class Program { static void Main(string[] args) { A a = new B(); //a只是A的对象,借助B实例化,无法调用B中的函数 a.SayHello(); a.Identify(); B b = new B(); b.SayHello(); b.Identify(); Console.ReadLine(); } }
对于override关键字,父类中需要有virtual,只能成对使用,含义是父类的方法被重写,声明是父类,实例化使用子类,那么调用时候父子可以共享,相当于提供了个父子沟通的通道(父子契约),但是只能使用子类的实现(重写)
具体实现依赖于虚表
public class A { public void SayHello() { Console.WriteLine("I'm A!"); } public virtual void Identify() { Console.WriteLine("from A"); } } public class B : A { public new void SayHello() { Console.WriteLine("I'm B!"); } public override void Identify() { Console.WriteLine("form B!"); } }