C#基础笔记(2)—— C# 类
一、C# 类
1. 什么是类class
2. 如何定义实例化一个class
class的实例化:定义一个类后,就必须实例化才能使用。实例化就是创建一个对象的过程。在C#中,使用new关键字来创建。
类 对象 = new 类 () ;
类的声明是以关键字class开始,后跟类的名称组成的。
类的实例是以关键字new开始,后跟类的名称组成的。
3. 类内的变量&函数
在类内声明的非静态变量,称之为 普通成员变量。
在类内声明的变量之前追加static关键字,称之为 静态成员变量。
在类内声明的非静态函数,称之为 普通成员函数。
在类内声明的函数之前追加static关键字,称之为 静态成员函数。
在类的内部声明的静态变量或函数,若想访问,必须通过类名来访问;普通成员变量或函数通过实例化的对象来访问。
4. 构造函数&析构函数
构造函数:
public 类名()
{
……
}
析构函数:
~类名()
{
……
}
// 构造函数 发生在new实例时,会被自动执行。 构造函数可以携带参数,当你显式地编写了自己的构造函数后,系统将不再提供默认的构造函数
// 析构函数 当当前类对象被销毁时,会被自动执行
// 作用域
// 函数内声明的变量(包括类对象),在执行完本函数时,会被自动销毁
// 但是,在类内声明的成员变量(包括类对象),只有当前类被销毁时,那它管理的其他变量(包括类对象)才会被销毁
// 如果不加命名空间,定义的类将在全局命名空间之下
namespace MySpace
{
public class Myclass
{
public int a;
// 构造函数
public Myclass() // 无参构造函数
{
Debug.Log("MySpace命名空间下的MyClass的构造函数执行");
}
public Myclass(int value) // 有参构造函数
{
a = value;
Debug.Log("a = ", a);
}
// 析构函数
~Myclass()
{
Debug.Log("MySpace命名空间下的MyClass的析构函数执行");
}
public void Show()
{
Debug.Log("MySpace命名空间下的MyClass的Show函数执行");
}
}
}
public class function : MonoBehaviour
{
void Start()
{
MySpace.Myclass myclass = new MySpace.Myclass();
int b = myclass.a;
myclass.Show();
}
void Update()
{
// Input 键盘鼠标监听工具类
if (Input.GetMouseButton(0)) // 0 鼠标左键 1 鼠标右键 2 鼠标滚轮
{
Destroy(this.gameObject);
}
}
}
5. 访问修饰符
所谓的访问修饰符,指的是 是否能够访问的到。
-
Public:任何公有成员可以被外部的类访问。
-
Private:只有同一个类的函数可以访问它的私有成员。
-
Protected:该类的内部和继承类可以访问。
-
internal:同一个程序集(命名空间)的对象可以访问。
-
Protected internal:3和4的并集,符合任意一条可以访问。
范围比较:
private < internal/protected < protected internal < public
protected限定的是只有在继承的子类中才可以访问,可以跨程序集 ;
internal限定的是只有在同一个程序集中才可以访问,可以跨类 。
二、C#面向对象
1. 继承
继承是面向对象程序设计中最重要的概念之一,继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得容易。同时也有利于重用代码和节省开发时间。
当创建一个类时,不需要完全重写新的成员变量和成员函数,只需要设计一个新的类,继承已有的类的成员即可。这个已有的类被称为 基类,这个新的类被称为 派生类。
C#中用“ : ”表示继承。C#不支持多重继承。
继承,就是将 共用的属性或方法抽离到基类 的过程,这个思维称之为面向对象。
2. 封装
封装,被定义为“把一个或多个项目封闭在一个物理的或逻辑的包中”。在面向对象程序设计方法论中,封装是为了防止对实现细节的访问。
封装,是将实现细节通过接口的方式暴露给第三方,而不需要关心实现细节。
封装和抽象是相辅相成的,抽象允许相关信息可视化,而封装则是使开发者实现所需级别的抽象。
C#的封装根据具体的需要,设置使用者的权限,并通过 访问修饰符 来实现。
访问修饰符
-
Public:任何公有成员可以被外部的类访问。
-
Private:只有同一个类的函数可以访问它的私有成员。
-
Protected:该类的内部和继承类可以访问。
-
internal:同一个程序集(命名空间)的对象可以访问。
-
Protected internal:3和4的并集,符合任意一条可以访问。
范围比较:
private < internal/protected < protected internal < public
虚函数
virtual代表虚函数,意味着 子类可以覆盖其实现,如果子类不覆盖,那将使用父类的同名函数。
子类使用override重写虚函数。
3. 多态
多态,是同一个行为具有多个不同表现形式或形态的能力;多态,就是同一个接口,使用不同的实例而执行不同操作。
-
静态多态(编译时)
在编译时,函数和对象的连接机制被称为早期绑定,也被称为静态绑定。C#提供了两种技术来实现静态多态性。分别为:函数重载和运算符重载。
-
动态多态(运行时)
在运行时,根据实例对象,执行同一个函数的不同行为。
运行时多态,在运行前无法确认调用哪个方法,只有在运行时才能确定的方法,这种行为称之为动态多态。
具体实现为,将派生类实例化对象赋给基类实例化的对象,用后者调用继承的方法。
// 注意: 只能子类给父类对象赋值,不能反过来
Rectangle rectangle = new Rectangle();
Triangle triangle = new Triangle();
Polygon baseParent1 = rectangle;
Polygon baseParent2 = triangle;
baseParent1.Show(); // 结果为rectangle show
baseParent2.Show(); // 结果为triangle show
4. 重载和覆盖
覆盖,发生在继承关系中,通过virtual和override实现,函数名和函数参数一模一样;
重载,发生在任何关系中,只要保证函数名字一致,参数不一致(带参数和不带参数,带参数顺序不一致,或者参数个数不一致),即可实现重载。
5. this和base关键字
this:可访问当前类能访问到的属性和方法;
base:只能访问基类的属性和方法。
三、C#类的更多表现形式
1. 静态类
类可以声明为static,这将变成一个静态类,不得被继承,特点是仅包含静态成员或常量。
静态类的特点:
-
不能被实例化,意味着 不能使用new关键字创建静态类的实例;
-
仅包含静态成员或常量;
-
不能被继承;
-
不能包含实例构造函数,但可以包含静态构造函数;
-
静态构造函数不可被调用。
静态类一般用于 工具类。
补充知识点:
-
const
在类内声明的const常量
-
(1)外部访问时,必须通过类名进行访问;
-
(2)只能在声明时初始化,不允许在任何其他地方对其初始化(包括构造函数);
-
(3)在某种程度上,被const修饰的变量(常量)为不可变值。
-
readonly
在类内声明的readonly常量
-
(1)readonly const不可共同修饰一个数据类型(基本数据量类型 + 自定义数据类型);
-
(2)readonly修饰的类型,可以被类的实例进行访问,但不可修改值;
-
(3)readonly的初始化只能发生在构造函数或声明中。
-
变量访问修饰符的控制
-
可通过访问修饰符构成的语法块,来实现类似 外部只读的效果。get set 以及学习 value赋值和访问代码的执行流程
public int mValue3 {get; private set;}
-
静态构造函数
-
(1)静态构造函数不需要增加访问修饰符;
-
(2)静态构造函数无论多少实例,都只被系统自动调用一次。
-
静态类:
-
(1)静态类不允许有实例构造函数,也不能有析构函数,只允许存在一个静态构造函数(静态类的静态构造函数不会执行);
-
(2)静态类不允许被实例化;
-
(3)静态类中的成员必须是 静态成员或常量;
-
(4)静态类无法作为基类派生。
2. 密封类
类可以被声明为sealed,这将变成为一个密封类。
密封类的特点:
-
不能被继承,但可以继承别的类或接口;
-
密封类不能声明为抽象类;
-
密封类内的成员函数,不能声明为sealed
密封类一般用于 防止重写某些类或接口影响功能的稳定。
密封类:
-
不允许被继承
-
sealed和abstract无法共存;
-
密封类内的函数,不允许增加sealed关键字;
-
密封类 可以正常继承 常见类(普通类、抽象类)接口;
3. 抽象类
类可以被声明为abstract,这将变成一个抽象类。
抽象类的特点:
-
不能被实例化,意味着 不能使用new关键字创建实例;
-
可只提供部分函数实现,也可仅声明抽象函数。
抽象类一般用在什么地方?
抽象类是提炼出了一些类共有的属性或函数接口的组织,为子类提供设计思路,配合多态多用于代码架构设计。
抽象类 1. 不允许实例化 2. 支持构造函数 3. 抽象类可继承抽象类 4. 静态构造函数只执行一次,但是其他的构造函数则根据不同实例,分别再次调用 5. 允许存在 virtual 虚函数 6. 若函数声明为abstract,则不允许包含函数体,子类必须显式覆盖父类的该方法。
4. 泛型类
类名后可以添加<T1, T2, T3……>,这将变成一个泛型类。泛型T1,T2,T3可以通过where关键字 来限定类型。
泛型类的特点:
-
在声明时可以不指定具体类型,但是在new实例化时必须指定T类型;
-
可指定泛型类型约束;
-
如果子类也是泛型,那么继承的时候可以不指定具体类型。
泛型类一般用于 处理一组功能一样,但类型不同的任务。
// 需求:
// 在类中定义一个数组,让这个类具备设置数据和访问数据的能力。
/* .NET含有以下5种泛型约束
* where T : class | T必须是一个类
* where T : struct | T必须是一个结构类型
* where T : new() | T必须要有一个无参构造函数
* where T : NameOfBaseClass | T必须继承名为NameOfBaseClass的类
* where T : NameOfInterface | T必须实现名为NameOfInterface的接口
*/
public class MyClassType
{
public int a;
public MyClassType(int value)
{
this.a = value;
}
}
public class MyClass15<T> where T:MyClassType
{
private T[] m_array;
public MyClass15(int size)
{
m_array = new T[size];
}
public void Set(int index, T value)
{
m_array[index] = value;
}
public int Get(int index)
{
return m_array[index].a;
}
}
public class TClass : MonoBehaviour
{
void Start()
{
/*MyClass15 myClass = new MyClass15(5);
myClass.Set(0, 1);
myClass.Set(1, 2);
int a = myClass.Get(0);
int b = myClass.Get(1);
Debug.LogFormat("第{0}位,值为{1}", 0, a);
Debug.LogFormat("第{0}位,值为{1}", 1, b);*/
/*MyClass15<string> myClass = new MyClass15<string>(5);
myClass.Set(0, "哈哈哈");
myClass.Set(1, "嘿嘿嘿");
string a = myClass.Get(0);
string b = myClass.Get(1);
Debug.LogFormat("第{0}位,值为{1}", 0, a);
Debug.LogFormat("第{0}位,值为{1}", 1, b);*/
MyClass15<MyClassType> myClass = new MyClass15<MyClassType>(5);
myClass.Set(0, new MyClassType(1));
myClass.Set(1, new MyClassType(2));
int a = myClass.Get(0);
int b = myClass.Get(1);
Debug.LogFormat("第{0}位,值为{1}", 0, a);
Debug.LogFormat("第{0}位,值为{1}", 1, b);
}
}
// 还有一种情况,基类是泛型类
public class Parent<T, X>
{
}
public class Child1<T, X> : Parent