类的继承与访问控制
类的继承
- 使用
sealed
修饰的类,是私有类,不能作为基类使用 - C#中一个类,只能有一个父类,但是可以实现多个接口
- 子类的访问级别不能超过父类的访问级别
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HelloOOP {
internal class Program {
static void Main(string[] args) {
//从代码上验证继承关系
Type t = typeof(Car);
Type tb = t.BaseType;
Type ttTop = tb.BaseType;
Console.WriteLine(ttTop.FullName);
/* 所有的类都继承自Object
* 并且Object上面就没有父类了,Object是处于最顶端的
*/
Console.WriteLine(ttTop.BaseType == null);
/* 是一个 is a, 一个子类的实例也是父类的实例
* 一个学生一定是一个人
* 一个父类的实例不一定是子类的实例
* 一个人不一定是一个学生
*/
Car car = new Car();
Console.WriteLine(car is Vehicle);
}
}
class Vehicle { }
//Car类继承自Vehicle类
class Car : Vehicle { }
// 1.私有类,不能当做基类来使用
sealed class Bycicle { }
}
成员的继承与访问
继承
继承的本质:派生类在基类已有的成员的基础之上,对基类进行的横向和纵向上的扩展
-
当继承发生的时候,子类对父类所有的成员是全盘继承的,除了三种东西不会被继承
- 静态构造器:用于初始化类的静态数据。
- 实例构造器:在创建类的新实例时调用。 每个类都必须定义自己的构造函数。
- 析构函数(终结器):由运行时的垃圾回收器调用,用于销毁类实例。
-
在派生的过程当中,我们进行的是扩展
也就是不能对父类的成员进行去除,只能扩展,这是静态类型语言(c++、c#、Java)的特点
而某些动态语言(python、JavaScript)则可以去除父类的成员
-
横向扩展即,类成员的增加;纵向扩展即,类成员的重写
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HellloAccess {
internal class Program {
static void Main(string[] args) {
Car car = new Car();
Console.WriteLine(car.Owner);
}
}
class Vehicle {
public Vehicle()
{
this.Owner = "N/A";
}
public string Owner { get; set; }
}
class Car : Vehicle {
public Car() {
//this.Owner = "Car Owner";
}
public void ShowOwner() {
//只能访问上一级的对象
Console.WriteLine(base.Owner);
/* 创建子类对象的时候,是从基类的构造器开始
* 一层一层往下构造的,最终构造出来子类的对象
*/
Console.WriteLine(this.Owner);
/* 当前这个base.Owner和this.Owner在这个例子中
* 都指向的是同一个字符串即"Car Owner"
* 因为子类中的构造器把原来的Owner覆盖了
*/
}
}
}
实例构造器无法被继承
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace HellloAccess {
internal class Program {
static void Main(string[] args) {
Car car = new Car("Ygc");
Console.WriteLine(car.Owner);
/* 实例构造器无法被继承
* 但是会先调用父类的实例构造器,然后再调用子类的实例构造器
*/
}
}
class Vehicle {
public Vehicle(string owner)
{
this.Owner = owner;
}
public string Owner { get; set; }
}
class Car : Vehicle {
/* 当父类的构造器,是带参数的。
* 那么父类的默认的构造器就会被覆盖掉
* 那么此时调用子类的默认构造器是不行的
* 因为调用子类的默认构造器,会先调用父类的默认构造器
* 而此时,父类的默认构造器是不存在的,会发生冲突
*/
//更改的办法:
public Car() : base("N/A") {
//this.Owner = "Car Owner";
}
//或者写一个带参数的构造器
public Car(string owner) : base(owner)
{
}
public void ShowOwner() {
Console.WriteLine(this.Owner);
}
}
}
继承中的访问级别
派生类和派生记录不能具有高于其基类型的可访问性。
虽然基类的其他所有成员都可供派生类继承,但这些成员是否可见取决于它们的可访问性。 成员的可访问性决定了其是否在派生类中可见。
类成员的访问级别是以类的访问级别作为上限的。
internal class Student{
public string name;
//所以这个类成员是不能被其他程序集所访问的
}
父类中的私有字段被限制在了父类的类体中,子类是不能直接访问的,但是是继承了的
以下代码可以证明:
namespace MyLib
{
public class Vehicle
{
private int _rpm;
//命名前面加_表示,这是一个私有字段
public void Accelerate() {
_rpm += 1000;
}
//该属性返回的是私有字段/ 100之后的值
public int Speed { get { return _rpm / 100; } }
}
public class Car : Vehicle{ }
}
using System;
using MyLib;
namespace HellloAccess {
internal class Program {
static void Main(string[] args) {
Car car = new Car();
car.Accelerate();
car.Accelerate();
Console.WriteLine(car.Speed);
/* 使用car对象调用父类的属性,能够成功返回
* 说明,父类的私有字段实际上也是被子类所继承的,
* 只是不能被car类所访问
*/
}
}
}
类成员的访问级别:
-
只有在基类中嵌套的派生类中,私有成员才可见。 否则,此类成员在派生类中不可见。
private
会把访问级别限制在类的类体中,所以只有嵌套的派生类才能访问public class A { private int _value = 10; public class B : A { public int GetValue() { return _value; } } } public class C : A { // public int GetValue() // { // return _value; // } } public class AccessExample { public static void Main(string[] args) { var b = new A.B(); Console.WriteLine(b.GetValue()); } } // The example displays the following output: // 10
-
受保护成员仅在派生类中可见。
会把访问级别限制在继承链上,并且可以夸程序集
protected
更多的用在方法上namespace MyLib { public class Vehicle { protected int _rpm; private int _fuel;//表示油量 //加油的方法 public void Refuel() { _fuel = 100; } /* 烧油方法 * 即不想暴露给外界,引发错误的调用 * 又想继承给子类继续调用 * 于是就用,protected访问修饰符 */ protected void Burn(int fuel) { _fuel -= fuel; } public void Accelerate() { Burn(1);//普通加速耗油1 _rpm += 1000; } public int Speed { get { return _rpm / 100; } } } public class Car : Vehicle{ public void TurboAccelerate() { //烧两次油,加速3000 Burn(2);//涡轮增压加速,耗油2 _rpm += 3000; } } } using System; using MyLib; namespace HellloAccess { internal class Program { static void Main(string[] args) { Car car = new Car(); car.Refuel();//先加油 car.TurboAccelerate();//再加速 Console.WriteLine(car.Speed); Bus bus = new Bus(); bus.Refuel(); bus.SlowAccelerate(); Console.WriteLine(bus.Speed); Console.ReadKey(); } } class Bus: Vehicle { public void SlowAccelerate() { //夸程序集,仍然可以调用父类的protected的成员 Burn(1); _rpm += 500; } } }
-
内部成员仅在与基类同属一个程序集的派生类中可见, 在与基类属于不同程序集的派生类中不可见。
-
公共成员在派生类中可见,并且属于派生类的公共接口。 可以调用继承的公共成员,就像它们是在派生类中定义一样。
public class A { public void Method1() { // Method implementation. } } public class B : A { } public class Example { public static void Main() { B b = new (); b.Method1(); } }
访问修饰符(全)
这一段是我抄的微软官方文档
- public:同一程序集中的任何其他代码或引用该程序集的其他程序集都可以访问该类型或成员。 某一类型的公共成员的可访问性水平由该类型本身的可访问性级别控制。
- private:只有同一
class
或struct
中的代码可以访问该类型或成员。 - protected:只有同一
class
或者从该class
派生的class
中的代码可以访问该类型或成员。 - internal:同一程序集中的任何代码都可以访问该类型或成员,但其他程序集中的代码不可以。 换句话说,
internal
类型或成员可以从属于同一编译的代码中访问。 - protected internal:该类型或成员可由对其进行声明的程序集或另一程序集中的派生
class
中的任何代码访问。 - private protected:该类型或成员可以通过从
class
派生的类型访问,这些类型在其包含程序集中进行声明。
各种东西的默认访问级别
默认情况下,类成员和结构成员(包括嵌套的类和结构)的访问级别为 private
。 不能从包含该类型的外部访问私有嵌套类型。
面对对象的实现风格
面向对象语言学习方法的问题
不要因为学了某个语言,就极大的夸赞该语言的优点,而藐视其他语言的缺点。
C#中的派生和继承,仅仅只是众多语言派生和继承的一种风格,还有其他的派生和继承的风格。
**C#使用的是Class-based
风格,基于类的封装、继承和多态。**目前是主流,C++,C#,Java。
JavaScript使用的是Prototype-based
风格,基于原型的封装、继承和多态的方式
Java不是单根的类型系统,它的引用类型是单根的,但是它还有一套系统,叫基本类型系统。
有部分内容,我是抄的微软的官方文档。