Class 专题
类的表现形式
静态类
关键字:static,主要用于工具类(如各类公式、基本不会发生变化的 构成的接口集)
特点:
- 不能被实例化,即不可new
- 不允许有实例构造函数,只允许存在一个静态构造函数
- 仅包含静态成员或常量
- 不能被继承
- 静态构造函数不会执行,也不可被调用
抽象类
关键字:abstract
特点:
-
不能被实例化,即不能使用new关键字创建实例
-
支持构造函数
public abstract class MyClass { public MyClass(){} //构造函数 static MyClass(){} //静态构造函数 }
-
抽象类可被继承,但静态构造函数只执行一次,其他构造函数则根据实例数调用相应的次数
public abstract class MyClass { public MyClass(){} //构造函数 static MyClass(){} //静态构造函数 } public abstract class MyClass1 : MyClass { public MyClass1(){} //构造函数 static MyClass1(){} //静态构造函数 } public class MyClass2 : MyClass { public MyClass2(){} //构造函数 static MyClass2(){} //静态构造函数 }
-
抽象类允许虚函数
-
抽象类中的函数若声明为abstract,则该函数不允许有结构体,其子类必须显示覆盖该方法
public abstract class MyClass { public abstract void MyFunc(); // 抽象函数不许有函数结构体{} } public class MyClass1 : MyClass { public override void MyFunc() { Debug.log("子类必须显示覆盖父类的抽象方法"); } }
密封类
关键字:sealed,用于防止重写某些类或接口,影响功能的稳定
特点
- 不能被继承,但可以继承别的类或接口
- 不能声明为抽象类
- 类内的成员函数不得声明成sealed
泛型类
用于处理一组功能一样,仅类型不同的任务
特点:
-
声明是可以不指定具体的类型,但是在new实例化时,必须指定T类型,T不仅可以为数据类型,还可以为类
-
可指定泛型类型约束(关键字:where)
泛型类型约束种类 描述 where T : class T必须是一个类 where T : struct T必须是一个结构类型 where T : new() T必须要有一个无参数的构造函数 where T : NameOfBaseClass T必须继承名为NameOfBaseClass的类 where T :NameOfInterface T必须实现名为NameOfInterface的接口 -
如果子类也是泛型的,那么继承的时候可以不指定具体类型,反之,则需要指定
//父类为泛型 public class Father<T,X> { } //子类为泛型,可不指定具体类型 public class Son1<T,X> : Father<T,X> { } //子类非泛型,需要指定具体的类型,如int, string public class Son2 : Father<int, string> { } //子类可增加自己所需的泛型 public class Son3<T,X,Y,A> : Father<T,X> { } public class Son4<T,X> : Father<int, string> { }
基础用法举例
//需求:在类里面定义一个数组,让这个类具备设置数据和访问数据的能力
//分析:数组中的数据可能是多种类型,所以需要用到泛型,本示例以int、string和类为例
public class MyArray<T>
{
//声明一个数组,类型为泛型T
public T[] myArray;
//在其初始化的时候设置数组长度
public MyArray(int size)
{
myArray = new T[size];
}
//设置数据,需要数组的下标,以及数组的值
public void Set(int index, T value)
{
myArray[index] = value;
}
//访问数据,只需要数组的下标
public T Get(int index)
{
return myArray[index];
}
}
public class MyClass
{
public int a;
public MyClass(int value)
{
this.a = value;
}
}
public class TClass : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
//int型
MyArray<int> myArrayInt = new MyArray<int>(5);
myArrayInt.Set(0, 1);
myArrayInt.Set(1, 2);
int aInt = myArrayInt.Get(0);
int bInt = myArrayInt.Get(1);
Debug.LogFormat("Int型的第一、二个值分别为{0}、{1}", aInt, bInt);
//string型
MyArray<string> myArrayString = new MyArray<string>(5);
myArrayString.Set(0, "一");
myArrayString.Set(1, "二");
string aString = myArrayString.Get(0);
string bString = myArrayString.Get(1);
Debug.LogFormat("String型的第一、二个值分别为{0}、{1}", aString, bString);
//类
MyClass myClass1 = new MyClass(7);
MyClass myClass2 = new MyClass(8);
MyArray<MyClass> myArrayClass = new MyArray<MyClass>(5);
myArrayClass.Set(0, myClass1);
myArrayClass.Set(1, myClass2);
MyClass aClass = myArrayClass.Get(0);
MyClass bClass = myArrayClass.Get(1);
Debug.LogFormat("Class的第一、二个值分别为{0}、{1}", aClass.a, bClass.a);
}
}
接口
interface + name 约束一些行为规范时,可以用接口
特点
- 只声明接口函数,不包含实现
- 访问修饰符必须是public
- 接口成员函数的定义是派生类的责任,接口提供了派生类应遵循的标准结构
- 接口不可被实例化,即不可new
- 接口可继承其他接口,允许单一、多个继承
接口与抽象类的区别
区别 | 接口 | 抽象类 |
---|---|---|
变量 | 不允许 | 允许 |
构造函数 | 不允许 | 允许 |
函数实现 | 不允许 | 允许 |
访问修饰符 | 默认情况:public | 默认情况:private |
访问修饰符 | 不允许为private | 若为抽象函数,则不能为private,反之则可以 |
多重继承 | 允许继承多个接口 | 不允许继承多个类 |
接口与抽象类的相同点
- 都不允许实例化
- 都支持只声明函数,不包含实现
- 关于派生类,都必须去实现接口或抽象类的方法
Struct结构体
struct + name{},
是***值类型***数据结构。它使得一个单一变量可以存储各种数据类型的相关数据。
特点:
- 结构体可带有方法、字段、索引、属性、运算符方法和时间
- 结构体可定义构造函数,但不能定义析构函数。不能定义无参构造函数。无参构造函数是自动定义的,且不能被改变
- 结构体不能继承其他的结构或类,但是可以实现一个或多个接口
- 结构不能作为其他结构或类的基础结构
- 结构成员不能指定为 abstract、virtual、protect
- 结构体不能通过new来实例化
struct和class的区别
区别 | struct | class |
---|---|---|
构造函数 | 不允许定义无惨构造函数,只允许定义有参构造函数 | 都可以 |
析构函数 | 不允许 | 允许 |
函数修饰符 | 不允许声明为virtual虚函数、protect函数 | 允许 |
类型修饰符 | 不允许声明为abstract抽象结构体 | 允许 |
普通变量 | 声明的全局普通变量,不能在声明式直接赋值,只能在构造函数中赋值 | 都可以 |
readonly | 只能在构造函数中赋值 | 都可以 |
继承 | 不能互相继承,只能继承接口 | 可以继承类和接口 |
访问变量 | 给成员变量显示赋值,可直接访问,若不通过new初始化,则不可以直接访问其内部变量(除const) | 必须先实例化new |
new | 是值类型,不会在堆上分配内存,仅仅是调用结构体的构造函数进行初始化 | 是引用类型,会在堆上分配内存,并调用结构体的构造函数进行初始化 |
struct和class的区别相同点
- 都支持静态构造函数
- 都支持自定义函数
- 结构体和类对***const***修饰的变量使用方式一样
- 访问函数:结构体变量和类对象必须进行初始化,才可访问
值类型和引用类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PkSTDwnR-1670585924958)(D:\书籍\笔记\C#\相关图片\值类型和引用类型.jpg)]
类型转换
is
可检测值类型和引用类型,成功返回true,否则返回false
as
首先判断原数据类型是否是目标数据类型,不是的话,编译器直接报错
as转换成功,返回源数据类型存储的数据类型,否则返回空
强制类型转换(显式类型转换)
-
***高精度***转换为***低精度***需要强制类型转换
(注)若想转换成功,需要高精度数据类型的位数 <= 低精度数据类型的位数
int a = 123; // 高精度 short b = (short)a; // 低精度
自动类型转换(隐式类型转换)
- ***低精度***转换为***高精度***需要强制类型转换,系统自动转换
short a = 1234; // 低精度
int b = a; // 高精度
装箱和拆箱
装箱
值类型转换成引用类型,发生GC(内存分配)
拆箱
引用类型转换成值类型
int a = 20;
object b = (object)a;//装箱,显式转换
object b = a; //装箱,隐式转换
int c = (int) b; //拆箱
堆和栈
a; // 低精度
#### 自动类型转换(隐式类型转换)
1. ***低精度***转换为***高精度***需要强制类型转换,系统自动转换
```c#
short a = 1234; // 低精度
int b = a; // 高精度
装箱和拆箱
装箱
值类型转换成引用类型,发生GC(内存分配)
拆箱
引用类型转换成值类型
int a = 20;
object b = (object)a;//装箱,显式转换
object b = a; //装箱,隐式转换
int c = (int) b; //拆箱