IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> [Unity] C#中级编程 - 06 - 隐藏/虚拟/抽象/覆写/密封 -> 正文阅读

[游戏开发][Unity] C#中级编程 - 06 - 隐藏/虚拟/抽象/覆写/密封

[Unity中文课堂教程] C#中级编程 - 06 - 隐藏/虚拟/抽象/覆写/密封

参考《c语言中文网》有很多实用的知识点

C# base关键字:调用父类成员方法 (biancheng.net)

C# virtual关键字详解 (biancheng.net)

继续上一篇笔记——继承没补充的细节。

隐藏:new

上一篇笔记讲到,子类继承父类后,子类可以直接访问父类的属性和方法,作出“宛如父类的内容根据访问修饰符直接拷贝到了子类中”的比喻。其实这是不恰当的思路,因为这就暗示子类和父类是同一个作用域,但不是的。

写代码的都应该知道,一个代码块一个作用域,最常见的划分区域的方法是花括号{},python是缩进。而代码寻找内容时会从内到外,从小到大的遍历作用域,寻找可用的内容,变量或函数等。

  • 父类一个作用域,子类一个作用域。子类继承父类后,基于访问修饰符,可访问父类内的属性和方法。

  • 子类查找属性或方法时,先从子类作用域内查找,找不到时再到父类中查找。如果子类有与父类重名的属性或方法,那么子类会优先找到自身作用域的内容并返回,父类的就不会再找了。

脚本①

public class Exercise_4_9 : MonoBehaviour
{
	public static int num = 100;		// 静态
	public int num_0 = 10;				// 公开
	public int num_1 = 20;				// 
    
	/* 更多的访问修饰符例子不一一列举了 */	
	public void get_num()
	{
		Debug.Log(num +"父类"+ num_0);
	}
}

脚本②

public class Exercise_4_10 : Exercise_4_9
{
	new public static int num = 102;	// 不加new也有效果,但会有警告
	new public float num_0 = 30;		// 屏蔽父类的属性,属性不论类型,就像方法不论返回值。
    
    /* 如果去掉注释,实例化调用中,就会调用子类的函数,不会调用父类的。
    public void get_num()
	{
		Debug.Log(num_1 +"子类"+ num_0); // 20子类30
	}*/
    
    public void get_num(int x)			// 继承中也适用重载,这种情况下便不会认为是同一个方法,
    {}
}

脚本③

public class Exercise_4_11 : MonoBehaviour
{
    void Start()
    {
		Exercise_4_10 myClass = new Exercise_4_10();

		Debug.Log(myClass.num_1 +"实例"+ myClass.num_0);			// 20实例30
		Debug.Log(Exercise_4_10.num +"静态"+ Exercise_4_9.num);	// 102静态100,两个不共用
		myClass.get_num();										 // 100父类10
    }
}

如果重名又没使用new,则会报警告'属性或方法' hides inherited member '属性或方法'. Use the new keyword if hiding was intended.(…隐藏继承的成员…如果要隐藏,请使用new关键字)。使用new可以消去警告。

  • 总结上面例子中的现象:
  1. 子类中有重名的属性或方法(属性的命名相同,方法的命名和参数相同),父类的就访问不了;静态属性和方法会相互独立,允许单独访问;局部属性和方法则被隐藏无法访问

想起,为什么有一种编程习惯:在类中想查看一个属性的值时不会直接访问类属性,而是为它专门写一个方法,只有一条返回语句,返回该属性

之前我还觉得这个操作是不是多此一举,现在想想,可能是避免这种被隐藏的问题,养成好习惯。而且单独写允许返回值作一定运算,就像c#中是set关键字。

  1. 通过子类的实例化访问父类的方法,该方法属于父类作用域。父类方法内调用的重名属性是父类中的

指定访问:this/base

上一个知识点讲到,因为 “访问顺序从内到外” ,子类无法访问到父类的重名属性或方法。

  • c#有个两个关键字:basethis,分别代表当前类的父类当前类。如果想指定访问父类或子类的内容,可以在属性或方法前面加上这个前缀。

脚本②:(在上个例子的脚本②中添加如下)

/* 屏蔽父类的 get_num 方法 */
new public void get_num()
{
	base.get_num();								  // 100父类10,调用父类的重名方法
	Debug.Log(this.num_1 +"子类"+ base.num_0);	// 20子类10
    // 当前类的 num_1 属性其实就算父类的,所以写不写,或写 this / base 都可以。
}

在unity中,经常用到this获取当前对象。在类的继承中,base也会经常用到。

  • base更加常见的用法是调用父类的某些初始化函数。在unity中就是Awake()之类的。

覆写:virtual/override

如果子类想隐藏、覆盖父类的属性或方法,有更加正式的方法。《C# virtual关键字详解 (biancheng.net)

  • virtual(虚拟)和override(覆写),两个关键字搭配使用。前者写在父类中,后者写在子类中。

脚本①:(基于修改,省略其他)

// public virtual int num_0 = 100;		// 报错,修饰 属性需要采用下面的写法
public virtual int num_10 {get;set;}	// 也就是不能给初始化值了?

/*
public virtual int num_10 {get;set;}	// 报错,不可修饰 静态
virtual public static void him()		// 报错
{}
*/

// public virtual void get_num()		// 也行
virtual public void get_num()			// virtual 关键字位于访问修饰符前后都可以
{/* 略 */}

脚本②

// override public void get_num()	// 也行
public override void get_num()		// override 对应父类中的重名方法 virtual
{/* 略 */}

如果重名不写关键字override,则会报警告,和本篇第一个知识点类似,只要加上就可以消去了; '属性或方法' hides inherited member '属性或方法'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.(…隐藏继承的成员…要使当前成员重写该实现,请添加override关键字。否则添加new关键字)

  • 总结特点

    1. virtualoverride不可修饰静态,可修饰属性,但需要特殊定义方式。

    会报错,提示cannot be marked as override, virtual, or abstract;(静态)不能将其标记为覆盖、虚拟或抽象。

    1. 可位于访问修饰符前后,按照传统编程习惯,放后面

    2. 父类方法关键字virtual,子类没有对应的override重名方法,正常 使用父类方法。

    3. 父类方法没有关键字virtual,子类对应的override重名方法,直接 编译报错。

    4. 父类方法关键字virtual,子类没有对应的override重名方法,但是参数返回值不同,不会当做重载函数,会直接编译报错

  • 从使用结果来看是一样的,virtual/override用不用都可以,就算不用程序也不会报错。利用第一个知识点的new,可以方便快捷的隐藏父类的属性方法。对第二个知识点的base使用也没有障碍。

  • 但是!!!从总结可以看出,关键字virtual/override可以实现程序自检

    1. 命名:命名不对会编译错误,因为没有对应的virtual修饰的需要重写的名字。
    2. 返回值参数:不一样也会编译错误。
    3. 提示父类有同名:如果父类有virtual,子类有重名但没override,会报警告。
  • 为了程序的可读性和封装性,使用正确的关键字修饰是很有必要的!!!

抽象:abstract/override

查覆写时看到类似的知识点,简单列举《C# abstract:声明抽象类或抽象方法 (biancheng.net)

  • 有时父类的同名属性方法只是为了提示子类覆写用,并没有实际内容。此时可使用abstract关键字:抽象

脚本①

// abstract public class Exercise_4_9 : MonoBehaviour  也可以
public abstract class Exercise_4_9 : MonoBehaviour	// 该类存在抽象属性或方法,一定也为抽象类
{	
	public static int num = 100;				// 抽象类中可以存在非抽象内容
	public int num_0 = 10;				
	public int num_1 = 20;
	public virtual int num_10 {get;set;}
    
	// public abstract int num_10 {get;set;}	// 报错,抽象只能修饰类或方法,不能修饰属性
	// public abstract int num_10 = 10;		
    
	public abstract void get_num();				// 抽象方法
	// abstract public void get_num();			// 也可以
	/*{
		Debug.Log(num +"父类"+ num_0);		// 不能释放注释,抽象方法不能写内容
	}*/
    
    // 同样不可修饰 静态,
}

脚本②

public class Exercise_4_10 : Exercise_4_9	// 继承抽象类,一般抽象类都是拿来继承的。
{
	public override void get_num()			// 必须有这个方法的实现,不然报错
	{
		// base.get_num();					// 不能调用抽象方法,编译错误。
		Debug.Log(this.num_1 +"子类"+ base.num_0);
	}
}

脚本③

public class Exercise_4_11 : MonoBehaviour
{
    void Start()
    {
		Exercise_4_10 myClass = new Exercise_4_10();
		// Exercise_4_9 myClass = new Exercise_4_9(); 报错,抽象类不可实例化,也就说只能继承……

		myClass.get_num();					// 20子类10
    }
}
  • 总结特点

    1. 不可修饰静态,(静态)不能将其标记为覆盖、虚拟或抽象。
    2. 不可修饰属性,只能修饰方法和类。若修饰了方法,那么该类也必须为抽象。抽象类和抽象方法共存
    3. abstract可放置访问修饰符前后,习惯性放后面,访问修饰符排第一。
    4. 抽象类不可实例化。通常只能用于继承了。
    5. 必须要有抽象方法的实现,不然报错。
    6. 组合关键字override使用。
  • 应用特点:实现程序自检,养成编程习惯。

    1. 避免模板类被实例化,会报错。
    2. 避免忘记实现抽象方法,会报错。
    3. 避免调用空的父类方法,会报错。
    4. 省去父类方法的实现,会报错。

覆写:override

  • 有时子类覆写了父类的方法后,该子类还可以成为下一个子类的父类,方法还可能再次被覆写。
  • override除了对应abstractvirtual外,还能对应override

从unity编译报错也能看出来,如果只有override时,会显示:because it is not marked virtual, abstract, or override;因为它没有被标记为虚拟、抽象或覆盖。

public class myclass_0 : MonoBehaviour
{
	public virtual void get_num()	// 虚拟或抽象方法
	{
	}
}

public class myclass_1 : myclass_0
{
	public override void get_num()	// 覆写
	{
	}
}

public class myclass_2 : myclass_1
{
	public override void get_num()	// 再覆写
	{
	}
}
  • 不过有时候又不希望被再覆写,就会用到下一个知识点——密封。

密封:sealed override

快速带过,《C# sealed:声明密封类或密封方法 (biancheng.net)

  • 当部分功能最后完成时,不会再改动时,将其设为密封。可以起到保护作用:密封类不可继承,密封方法不可覆写。
public sealed class myclass_1 : myclass_0			// 该类为密封类,不可用于继承
{
	// sealed public override void get_num()		// 也可以
	public sealed override void get_num()			// 该类为密封方法,不可再被覆写
	{
		Debug.Log("密封方法");
	}
}
  • 总结特点
    1. sealed必须修饰override使用。不然会报错: cannot be sealed because it is not an override;不能被密封,因为它不是覆盖。
    2. 密封方法可以存在普通类中,和抽象不一样,不一定类也要是密封。
    3. 密封类不可继承,密封方法不可覆写。

总结

  • 即使被虚拟、抽象、密封后,如果不想使用父类的同名属性方法,还是可以直接隐藏掉。
  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-12-15 18:37:40  更:2021-12-15 18:38:14 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 21:03:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码