一.引言
C#作为一个面向对象的语言,和Java差异不大,所以这里就不做赘述了,只是想整理一下C#中特别的语法
二.不同于其他面向对象语言的技巧收纳
- 可以声明一个和当前类同名的类对象,但是不能初始化,因为调用默认构造函数,会初始化属性,如果对当前同类型的属性进行实例化,会再次进入构造函数,直接内存溢出
class Person { //女朋友 //如果要在类中申明一个和自己相同类型的成员变量时 //不能对它进行实例化 public Person gridFriend; // 可以赋值为null //朋友 public Person[] boyFriend; } - 可以通过
default(类型)查看该类型的默认值,比如int默认值是0 - 关于构造函数的重载,C#和Kotlin这块类似,可以调用this的构造函数
需要记住的一点是,会优先调用this()构造函数,然后才回过来调用new对象时使用的构造函数,Kotlin也是如此class Person { public string name; public int age; public Person(int age) { //this代表当前调用该函数的对象自己 this.age = age; } public Person(string name) { this.name = name; } public Person(int age, string name) : this(age + 10) { Console.WriteLine("Person两个参数构造函数调用"); } } - 析构函数和垃圾回收
- 析构函数
这个只会在C#中使用到,Unity中不会使用,仅做了解
析构函数是在引用类型的堆内存被回收时调用// 基本语法如下 // ~类名() // { // } class Person { //当引用类型的堆内存被回收时 //析构函数 是当垃圾 真正被回收的时候 才会调用的函数 ~Person() { } } - 垃圾回收
垃圾回收在Java阶段已经充分了解过了,这里就不赘述,明白年轻代的复制清除法以及分代模型即可//手动触发垃圾回收的方法 //一般情况下 我们不会频繁调用 //都是在 Loading过场景时 才调用 // 避免游戏过程中发生STW现象 GC.Collect();
- 析构函数
- 成员属性
成员属性是C#中比较特别的,有点类似于Java中的Get/Set方法简单说,C#中的成员属性就是Java中的set/get方法的浓缩,将其与成员变量结合在一起,更简洁一点,很多时候也会直接声明成员属性而非成员变量// 基本语法 // 访问修饰符 属性类型 属性名 // { // get{} // set{} // } class Person { private string name; //属性的命名一般使用 帕斯卡命名法 public string Name { get { //可以在返回之前添加一些逻辑规则 //意味着 这个属性可以获取的内容 return name; } set // get set 还可以加权限修饰符,但范围要小于外层的修饰符 { //可以在设置之前添加一些逻辑规则 // value 关键字 用于表示 外部传入的值 name = value; // 这里的value类似于Kotlin中的field } }
成员属性代码链接 - 索引器
类似于成员属性和方法的结合体,针对类中有数组的情况,其实没有数组也可以很方便的获取到类对象中的一些状态值,所以针对不同的状态获取也是挺便捷的
功能:让对象可以像数组一样通过索引访问其中元素,使程序看起来更直观,更容易编写- 语法
//访问修饰符 返回值 this[参数类型 参数名, 参数类型 参数名.....] //{ // 内部的写法和规则和索引器相同 // get{} // set{} //} - 示例
class Person { private string name; private int age; private Person[] friends; private int[,] array; #region 知识点五 索引器可以重载 //重载的概念是——函数名相同 参数类型、数量、顺序不同 public int this[int i, int j] { get { return array[i, j]; } set { array[i, j] = value; } } public string this[string str] { get { switch (str) { case "name": return this.name; case "age": return age.ToString(); } return ""; } } #endregion public Person this[int index] { get { //可以写逻辑的 根据需求来处理这里面的内容 #region 知识点四 索引器中可以写逻辑 if (friends == null || friends.Length - 1 < index) { return null; } #endregion return friends[index]; } set { //value代表传入的值 //可以写逻辑的 根据需求来处理这里面的内容 if (friends == null) { friends = new Person[] { value }; } else if (index > friends.Length - 1) { //自己定了一个规则 如果索引越界 就默认把最后一个朋友顶掉 friends[friends.Length - 1] = value; } friends[index] = value; } } }
- 语法
- 静态类
使用static修饰的类,类似于Kotlin中用object修饰的类,类中只能包含静态的成分
静态构造函数- 静态类和普通类都可以有
- 不能使用访问修饰符
- 不能有参数
- 只会自动调用一次(只要调用静态类中的任何一个成分时,就会调用,主要用于初始化成员变量,非静态的类同理) 静态类与静态构造函数代码链接
- 拓展方法
和Kotlin中的扩展方法很像,唯一不同的就是写法上的差异- 语法
访问修饰符 static 返回值 函数名(this 拓展类名 参数名, 参数类型 参数名,参数类型 参数名....) - 示例
在C#中是通过一个静态类来包裹我们要扩展的方法,而Kotlin是在一个File中用法和Kotlin中的扩展方法一样static class Tools { //为int拓展了一个成员方法 //成员方法 是需要 实例化对象后 才 能使用的 //value 代表 使用该方法的 实例化对象 public static void SpeakValue(this int value) { //拓展的方法 的逻辑 Console.WriteLine("唐老狮为int拓展的方法" + value); } public static void SpeakStringInfo(this string str, string str2, string str3) { Console.WriteLine("为string拓展的方法"); Console.WriteLine("调用方法的对象" + str); Console.WriteLine("传的参数" + str2 + str3); } // 为自定义的一个类定义的拓展方法 public static void Fun3(this Test t) { Console.WriteLine("为test拓展的方法"); } }
注意:如果我们拓展的方法名和原来类中的成员方法重名后,会优先调用原本的成员方法,是覆盖不了的
- 语法
- 运算符重载
用得比较少,这里就做一个了解吧- 语法
public static 返回类型 operator 运算符(参数列表) - 示例
关于哪些运算符可以重载,哪些不可,详情见代码链接class Point { public int x; public int y; public static Point operator +(Point p1, Point p2) { Point p = new Point(); p.x = p1.x + p2.x; p.y = p1.y + p2.y; return p; } public static Point operator +(Point p1, int value) { Point p = new Point(); p.x = p1.x + value; p.y = p1.y + value; return p; } public static Point operator +(int value, Point p1) { Point p = new Point(); p.x = p1.x + value; p.y = p1.y + value; return p; } // 使用 Point p = new Point(); p.x = 1; p.y = 1; Point p2 = new Point(); p2.x = 2; p2.y = 2; Point p3 = p + p2;
运算符重载代码链接
- 语法
- 内部类和分部类
内部类已经司空见惯,这里主要是积累分部类- 分部类概念
简单说就是把一个类分成多个部分来写,也就是说同一个文件中可以存在很多个同名的类,不过得用partial来声明它是一个分部类,并且分布类中不能有重复的成员变量 - 示例
仅仅是做一个了解,分部方法有点特别,有点类似于C语言中方法要在头部声明一样,将声明和实现分离partial class Student { public bool sex; public string name; partial void Speak(); } partial class Student { public int number; partial void Speak() { //实现逻辑 } public void Speak(string str) { } }
详情看代码链接即可分部类和分部方法链接
- 分部类概念
- 里氏替换原则
从接口的角度来说就是面向接口编程,用父类承载子类,这里的示例语法和Kotlin很类似
任何父类出现的地方,子类都可以替代在接口层用的更多一点,或者抽象类class GameObject { } class Player : GameObject { public void PlayerAtk() { Console.WriteLine("玩家攻击"); } } class Monster : GameObject { public void MonsterAtk() { Console.WriteLine("怪物攻击"); } } class Boss : GameObject { public void BossAtk() { Console.WriteLine("Boss攻击"); } } #endregion class Program { static void Main(string[] args) { Console.WriteLine("里氏替换原则"); //里氏替换原则 用父类容器 装载子类对象 GameObject player = new Player(); GameObject monster = new Monster(); GameObject boss = new Boss(); GameObject[] objects = new GameObject[] { new Player(), new Monster(), new Boss() }; #region 知识点三 is和as //基本概念 // is:判断一个对象是否是指定类对象 // 返回值:bool 是为真 不是为假 // as:将一个对象转换为指定类对象 // 返回值:指定类型对象 // 成功返回指定类型对象,失败返回null //基本语法 // 类对象 is 类名 该语句块 会有一个bool返回值 true和false // 类对象 as 类名 该语句块 会有一个对象返回值 对象和null for (int i = 0; i < objects.Length; i++) { if (objects[i] is Player) { (objects[i] as Player).PlayerAtk(); } else if (objects[i] is Monster) { (objects[i] as Monster).MonsterAtk(); } else if (objects[i] is Boss) { (objects[i] as Boss).BossAtk(); } } #endregion } } - 继承中的构造函数
大部分和Kotlin的语法结构很类似,需要注意如下几点- 使用base指代父类,而非super,继承的构造方法的重载写法和Kotlin一样
- 对构造函数进行重载时,会优先执行冒号后面的this构造函数 继承中构造函数的代码链接
- 基本类型和引用类型的拆装箱
在Kotlin中基本类型是对象类型,但是如果不是可空的话,会自动使用基本的类型,C#中也可以将基本类型用object来承载,称之为装箱,强转为对应的基本类型称之为拆箱
注意:装箱会将栈内存会迁移到堆内存中,拆箱会将堆内存会迁移到栈内存中(拆装箱尽量少用,存在内存消耗)
C#类型拆装箱代码链接 - 密封类
Kotlin中也有,含义和功能都是一样
使用关键字sealed声明
主要作用就是不允许最底层子类被继承,可以保证程序的规范性、安全性
密封类代码链接 - 密封方法
关键字和密封类一样,用密封关键字sealed 修饰的重写函数,让虚方法或者抽象方法之后不能再被重写,特点是和override一起出现
密封方法代码链接 - 抽象类和多态
这里简称v(virtual)o(override)b(base)
这里需要说明一个点- 在C#中不仅有abstract抽象方法:具体行为完全由子类决定
- 还有virtual虚方法:子类可以重写进行覆盖,这也是多态的关键点
- 还有没有任何关键字声明的普通方法:父类中的普通方法只能被孩子继承使用,无法重写 多态代码链接
- 对命名空间namespace的理解
- 类似于Android中不同包与包之间的关系,如果想使用必须导包,或者导入命名空间
- 一个解决方案中可以有多个项目,项目与项目之间的关系就类似于Android中模块与模块的关系,相互独立,但是又可以进行联系
- 万类之父-Object
C#中有属于自己的特色的Object的成员方法和静态方法,详情看代码链接
万类之父Object相关代码链接