【Unity|C#】基础篇(3)——类(class)/ 接口(interface)
【学习资料】
《C#图解教程》(第4~7章):https://www.cnblogs.com/moonache/p/7687551.html
电子书下载:https://pan.baidu.com/s/1mhOmBG0
【内容】
- 所有类的基类
- 类的组成
- 类的实例化、内存分配
- 类的特性:封装、继承、多态
- 接口
- 扩展知识
- 隐藏基类的成员
- struct、class
- 抽象函数(abstract)、虚函数(virtual)
- 抽象类(abstract)、接口(interface)
【笔记】
所有类的基类: object
类由2部分组成
> 数据成员:存储与类或类的实例相关的数据
> 函数成员:执行代码

类的实例化
> 声明一个类的引用,指向空数据null

> new数据部分,并将引用指向数据

【类的特性】
类的三大特性:封装、继承、多态
> 封装
> 访问修饰符
> 继承
> 派生子类
> 构造/析构函数
> 多态
> 静态多态性:函数重载(不同参数)、运算符重载
> 动态多态性:虚函数、抽象函数、隐藏方法
- 封装

- 继承
- this : 当前类对象
- base : 父类对象
- sealed:密封类,不能被继承
sealed class Entity
{
}
class Car : Entity // sealed不能被继承,会报错
{
}
- C#不支持类的多继承,但可以继承多个接口(interface)
- 父类初始化:在初始化列表里进行
public Tabletop(double l, double w) : base(l, w)
{ }- 构造函数
- 在创建类对象时,会执行构造函数:Car car = new Car()
- 构造顺序:先调用父类的构造,再调用子类的
- 析构函数
- 在释放对象时(GC垃圾回收),会执行析构函数
- 析构顺序:先调用子类的析构,再调用父类的
class Entity
{
// 默认构造函数,无参数
public Entity()
{
Debug.Log("new Entity");
}
// 带参构造函数
public Entity(int id)
{
Debug.Log("new Entity id=" + id);
}
// 重写析构函数,一个类只能有一个,且无参数
~Entity()
{
Debug.Log("delete Entity");
}
}
class Car : Entity // sealed不能被继承,会报错
{
// 不写父类的构造,默认调用默认构造函数Entity()
public Car()
{
Debug.Log("new Car");
}
// 在初始化列表: 调用父类的带参构造函数
public Car(int id)
: base(id)
{
Debug.Log("new Car");
}
// 重写析构函数,一个类只能有一个,且无参数
~Car()
{
Debug.Log("delete Car");
}
}
- 多态
静态多态性
函数重载(不同参数)
public Tabletop(double l, double w) : base(l, w)
{ }
- 运算符重载
public static Box operator +(Box b, Box c)
{ }
- 动态多态性:子类重写父类方法
- 虚函数
- 关键字:virtual / override
- 子类重写虚函数后:不管在父类中调用,还是在子类中调用,执行的都是子类重写后的函数 (Car.Move)
- 调用父类虚函数的方法:通过 base 关键字调用(base.Move())
- 注:子类也可不重写父类的虚函数
class Entity
{
public void AI()
{
Move();
}
public virtual void Move() // 虚函数声明 virtual
{
Debug.Log("Entity.Move");
}
}
class Car : Entity
{
// 重写方法
public override void Move() // 重写虚函数 override
{
Debug.Log("Car.Move");
}
} // 测试Test
void Start()
{
Entity entity = new Car();
Car car = (Car)entity;
entity.Move(); // 输出:Car.Move
car.AI(); // 输出:Car.Move
}
- 虚函数
- 动态多态性:子类重写父类方法
- 抽象函数
- 关键字:abstract / override
- 注:父类只声明抽象函数,没有具体实现
- 注:存在抽象函数的类,必须声明为抽象类,且不能实例化成对象
// 含有抽象函数,必须声明为抽象类,且不能实例化对象
abstract class Entity
{
public abstract void Move(); // 抽象函数
}
class Car : Entity
{
// 子类必须实现父类的抽象函数
public override void Move()
{
Debug.Log("Car.Move");
}
} void Start()
{
// 报错,抽象类无法实例化
//Entity entity2 = new Entity(); Car entity = new Car();
entity.Move(); // 输出:Car.Move
}
- 抽象函数
- 隐藏方法 (不推荐,容易出错)
- 子类重写父类虚函数时,不写 override 关键字,编译器也会显示警告(warning)
- 注:具体执行父类/子类函数,根据调用环境决定(指向对象的引用类型、父类or子类其他函数中进行调用)
- 测试1:不同引用类型调用Move 、通过AI()函数中调用
class Entity
{
public void AI()
{
Move();
}
public virtual void Move()
{
Debug.Log("Entity.Move");
}
}
class Car : Entity
{
// 不写override
public void Move()
{
Debug.Log("Car.Move");
}
} void Start()
{
Entity entity = new Car();
Car car = (Car)entity;
entity.Move(); // 输出:Entity.Move
entity.AI(); // 输出:Entity.Move
car.Move(); // 输出:Car.Move
car.AI(); // 输出:Entity.Move
}
- 测试2:子类重写AI()函数
class Entity
{
public virtual void AI()
{
Move();
}
public virtual void Move()
{
Debug.Log("Entity.Move");
}
}
class Car : Entity
{
public override void AI()
{
Move();
}
// 不写override
public void Move()
{
Debug.Log("Car.Move");
}
} void Start()
{
Entity entity = new Car();
Car car = (Car)entity;
entity.Move(); // 输出:Entity.Move
entity.AI(); // 输出:Car.Move
car.Move(); // 输出:Car.Move
car.AI(); // 输出:Car.Move
}
- 隐藏方法 (不推荐,容易出错)
【接口】
> 关键字:interface
> 相当于是个规则,里面只能有:方法、属性、索引、事件
> 一个类只能继承一个父类,但可以实现多个接口
> 注:接口 也可以 继承另一个 接口
> 注:一个类继承了接口后,接口中所有的方法(包括属性、索引、事件)都必须实现
> 注:实现接口中的方法必须定义为 public
interface EntityInterface
{
int Value { get; set; } // 可以声明属性,但子类中必须实现
void Move(); // 不用写修饰符(public) //int value; // 不能定义变量
//void AI() { Move(); } // 不能实现接口函数
}
class Car : EntityInterface
{
// 必须实现接口中的属性,且必须为 public
public int Value
{
get;
set;
}
// 必须实现接口中的函数,且必须为 public
public void Move()
{
}
}
【扩展知识】
- 隐藏基类的成员
- 子类通过new关键字,隐藏父类的成员数据、方法
- 具体调用的是哪个?根据调用环境决定(指向对象的引用类型、父类or子类其他函数中进行调用)

- 案例1:不同引用类型获取.value、调用父类的PrintValue
class A
{
public int value = ;
public void PrintValue()
{
Debug.Log("A.value=" + value);
}
}
class B : A
{
public new int value = ;
}
void Start()
{
A a = new B();
B b = (B)a;
Debug.Log(a.value); // 1
Debug.Log(b.value); // 2
a.PrintValue(); // A.value=1
b.PrintValue(); // A.value=1
}
- 案例2:子类隐藏父类的PrintValue
class A
{
public int value = ;
public void PrintValue()
{
Debug.Log("A.value=" + value);
}
}
class B : A
{
public new int value = ;
// 隐藏父类函数
public new void PrintValue()
{
Debug.Log("B.value=" + value);
}
}
void Start()
{
A a = new B();
B b = (B)a;
a.PrintValue(); // A.value=1
b.PrintValue(); // B.value=2
}
- 案例3:子类重写父类的PrintValue
class A
{
public int value = ;
public virtual void PrintValue()
{
Debug.Log("A.value=" + value);
}
}
class B : A
{
public new int value = ;
// 重写父类函数
public override void PrintValue()
{
Debug.Log("B.value=" + value);
}
}
void Start()
{
A a = new B();
B b = (B)a;
a.PrintValue(); // B.value=2
b.PrintValue(); // B.value=2
}
- 案例1:不同引用类型获取.value、调用父类的PrintValue
struct 、class
类 是 引用类型,结构体 是 值类型
因此结构体不能=null,也不需要使用new
结构体 不支持继承
结构体 不能声明 默认(无参)构造函数,不能声明 析构函数
结构体成员 不能指定为 abstract、virtual 或 protected
抽象函数(abstract) 、虚函数(virtual)
abstract:父类只声明没有具体实现,子类必须重写实现父类中的抽象函数
virtual:子类可以不重写父类的虚函数
抽象类(abstract) 、接口(interface)
抽象类:依然是一个类,不能被实例化,它仍然包含类的函数
接口:相当于是个规则,里面只能有方法、属性、索引、事件
抽象类:有抽象的方法,也有不抽象的方法。子类必须实现父类的抽象方法
接口:继承了接口后,所有的接口方法都必须实现
一个类只能继承一个父类,但是可以实现多个接口
【Unity|C#】基础篇(3)——类(class)/ 接口(interface)的更多相关文章
- 新年在家学java之基础篇-高级类的特性
继承 extends 子类的共性代码都是继承自父类的,每个子类只要写自己特有的代码 class 子类 extends 父类 继承提高了代码的复用性,提供了多态的前提,但是不要为了某个功能去继承 子类不 ...
- 线程基础知识01-Thread类,Runnable接口
常见面试题:创建一个线程的常用方法有哪些?Thread创建线程和Runnable创建线程有什么区别? 答案通常集中在,继承类和实现接口的差别上面: 如果深入问一些问题:1.要执行的任务写在run()方 ...
- C#基础篇七类和静态成员
1.new关键字做的4个事情 1.1 开辟堆空间 a.开辟多大的空间呢? 当前类 所有的 成员变量类型所占空间的总和 + 类型指针(方法表的地址) b.开辟了空间干什么用呢? 存放 成员变量 1.2 ...
- JAVA基础篇 之 类的初始化
类中属性的隐式初始化,代码如下,我们看下不同类型默认的初始值是什么 创建一个Demo类如下: class Demo { int a; byte b; short c; long d; boolean ...
- 【Java_多线程并发编程】基础篇—Thread类中start()和run()方法的区别
1. start() 和 run()的区别说明 start()方法: 它会启动一个新线程,并将其添加到线程池中,待其获得CPU资源时会执行run()方法,start()不能被重复调用. run()方法 ...
- C# 类(9) - 接口 Interface
Interface 接口 类似 抽象类,也不能被实例化...(前面说的静态类,加上抽象类,还有这个,都3个了)接口其实比 抽象类 更加抽象.接口的方法(这个方法还不能有实体代码,和抽象类的抽象方法差不 ...
- 图解Python 【第五篇】:面向对象-类-初级基础篇
由于类的内容比较多,分为类-初级基础篇和类-进阶篇 类的内容总览图: 本节主要讲基础和面向对象的特性 本节内容一览图: 前言总结介绍: 面向对象是一种编程方式,此编程方式的实现是基于对 类 和 对象 ...
- cocos2dx基础篇(3) 常用重要类
---------------------------------------- 入口类main.cpp 主要控制类AppDelegate.cpp -------------------------- ...
- Hibernate.基础篇《一》.Hibernate工具类.
Hibernate.基础篇<一>.Hibernate工具类. 话述: Hibernate.基础篇第一篇,前面是代码.后面再加理论&实践. Hibernate使用的版本是:5.x,在 ...
随机推荐
- notepad中运行python, --kali安装后出现乱码
notepad中运行python cmd /k python "$(FULL_CURRENT_PATH)" & ECHO. & PAUSE & EXIT - ...
- 拍摄UFP 单一职责原则
3.1 新手机 3.2 拍摄 3.3 没用的东西 3.4 单一职责原则 就一个类而言,应该仅有一个引起它变化的原因, 3.5 方块游戏的设计 如果一个类承担的职责过多,就等于把这些职责耦合在一起,一个 ...
- go 算法与数据结构
数据结构 稀疏数组 package main import "fmt" /* 稀疏数组 案例:五子棋存盘与复盘 节省存储空间 */ type ValNode struct { ro ...
- cf959E
题意简述:一个包含n个点的完全图,点的编号从0开始,两个点之间的权值等于两个点编号的异或值,求这个图的最小生成树 规律是 ∑ i from 0 to n-1 (i&-i) #include & ...
- C语言再学习part2-重新认识C语言词汇
迷阳迷阳,无伤吾行.无行郗曲,无伤吾足.—庄子 C语言词汇: 标识符 在程序中的变量名.函数名.标号等等成为标识符.其中标识符相在C中只能是字母A~Z,a~z,数字0~9,下划线(_)组成的字符串,并 ...
- 剑指offer-面试题54-二叉搜索树的第k大节点-中序遍历
/* 题目: 求二叉搜索树的第k大节点. */ /* 思路: 中序遍历. */ #include<iostream> #include<cstring> #include< ...
- 论文阅读笔记(十八)【ITIP2019】:Dynamic Graph Co-Matching for Unsupervised Video-Based Person Re-Identification
论文阅读笔记(十七)ICCV2017的扩刊(会议论文[传送门]) 改进部分: (1)惩罚函数:原本由两部分组成的惩罚函数,改为只包含 Sequence Cost 函数: (2)对重新权重改进: ① P ...
- java在子类中,调用父类中被覆盖的方法
在java中,子类中调用与父类同名的方法(即父类中被覆盖的方法)用super来调用即可,下面是示例: 子类父类的定义 public class b { void show() { System.out ...
- 【13】正则化网络激活函数(Batch归一化)
正则化网络激活函数(Batch归一化): 在神经网络训练中,只是对输入层数据进行归一化处理,却没有在中间层进行归一化处理.要知道,虽然我们对输入数据进行了归一化处理,但是输入数据经过σ(WX+b)σ( ...
- send and recieve message with myself (python socket )
# socket server import socket sk = socket.socket() sk.bind(("127.0.0.1",8082)) sk.listen() ...