C#(GC,装拆箱,ref/out,容器,迭代器),Lua(元表,面向对象),算法,数据结构,渲染(流程),性能优化。
1.拆装箱
装箱 box指令
把值类型转换为引用类型
在堆上申请内存空间,把值类型复制到堆上
拆箱 unbox.any指令
把引用类型转换为值类型
把存储在堆上的引用类型转化为值类型,赋值给值类型变量
2.foreach
foreach语句中,第一次遇到Fruit对象时,调用GetEnumerator()(在Unity老的Mono编辑器中会产生GC,Unity5.6后已经修复),当每次执行到in关键字,调用MoveNext()方法,每次读取数据则调用Current属性。
3.结构体
结构体是值类型
结构体的特点:
- 结构体可定义构造函数,但不能定义析构函数。不能定义默认的无参构造函数。默认的构造函数是自动定义的,且不能被改变。
- 结构体不能继承其他的结构体或类
- 结构不能作为其他结构或类的基础结构。
- 结构可实现一个或多个接口。
- 结构成员不能指定为 abstract、virtual 或 protected。
- 当您使用 New 操作符创建一个结构对象时,会调用适当的构造函数来创建结构。与类不同,结构可以不使用 New 操作符即可被实例化。
- 如果不使用 New 操作符,只有在所有的字段都被初始化之后,对象才可以被使用。如果所有字段没有完成初始化,引用未赋值的字段会导致编译错误。
类和结构体的区别
- 类是引用类型,结构体是值类型。
- 结构体不支持继承。
- 结构体不能声明默认的无参构造函数。
- 结构体中声明的字段无法赋予初值,类可以
4.值类型和引用类型的区别
值类型在栈上分配空间,由系统自动分配,自动释放
引用类型在堆中分配内存,用new分配
string是特殊的引用类型,本质上是一个char类型的数组
string的remove方法,remove(3)从指定下标位置(包含)删除后面的字符串
remove(3,2)从指定位置(包含)删除后面2个字符
string的SubString(4)从指定下标位置(包含)获取后面的子字符串
SubString(4,2)从指定下标位置(包含)获取后面2个字符
5.可变参数 params,在有多个可变形参时,可变参数要放在最后
定义:public void Sum(params int[] arr) { }
使用:Sum(1,2,3,4,5);
6.析构函数
~Person() { }
1.每个类只能有一个析构方法
2.析构函数不能有返回值
3.析构函数不能有权限修饰符
4.析构函数不能带有参数,更不能重载
7.面向对象 面向过程
面向过程:分析解决问题所需要的步骤,然后用函数把这些步骤一步步实现。
面向对象:把构成问题的事物分解成各个对象,建立对象的目的不是为了完成一个个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。封装,继承和多态
8.封装
封装是防止对实现细节的访问,设置使用者的访问权限,并通过访问修饰符来实现。
(1) Pubilc :任何公有成员可以被外部的类访问。
(2) Private :只有同一个类中的函数可以访问它的私有成员。
(3) Protected :该类内部和继承类中可以访问。
(4) internal : 同一个程序集的对象可以访问。
(5) Protected internal :3 和 4 的并集,符合任意一条都可以访问。
9.抽象
- 抽象方法只能出现在抽象类中
- 抽象方法在父类中不能实现
- 抽象类不能直接实例化
- 抽象方法在继承抽象类的子类中必须实现
public abstract class Food
{
public abstract void Eat();
}
10.虚函数
- virtual修饰的方法必须有实现(哪怕仅仅添加一对大括号)
- virtual可以被子类重写,也可以不重写
- 只有方法和属性才能使用virtual修饰,字段(变量)不能使用virtual修饰
public class Food
{
public virtual string Name { get; set;}
public virtual void Eat() { }
}
11.接口
- 接口默认声明是public的
- 接口中定义的方法,不能添加访问修饰符,默认都是public
- 接口中的方法,在接口中不能实现
- 一旦某个类实现了接口,就必须实现接口中定义的全部成员方法
- 不能直接实例化接口
- 接口中只能声明方法,属性,事件,索引器。
- 接口不能包含字段、构造函数、析构函数、静态成员或常量。
public interface Food
{
string Name { get; set; }
void Eat();
}
12.内存区域
- 堆区,手动管理
- 栈区,系统管理
- 静态区,程序运行过程中,在内存中一直存在
- 程序代码区
- 常量区
13.泛型
- 使用泛型,可以保证类型安全
- 避免拆箱和装箱,提高性能
- 提高代码的重用
14.索引器
索引器是指允许一个对象可以像数组那样被索引,可以通过方括号[ ]来访问对象的实例。使用this关键字,索引器的行为的声明在某种程度上类似于属性(property)。就像属性(property),您可使用 get 和 set 访问器来定义索引器。但是,属性返回或设置一个特定的数据成员,而索引器返回或设置对象实例的一个特定值。
public T this[int index]
{
set{ }
get{ }
}
15.静态成员
我们可以使用 static 关键字把变量定义为静态的。当我们声明一个变量为静态时,意味着无论有多少个类的对象被创建,只会有一个该静态变量的副本。
也可以把一个成员函数声明为 static。这样的函数只能访问静态变量。静态函数在对象被创建之前就已经存在。
16.预处理器指令
预处理器指令指导编辑器在实际编译开始之前对信息进行预处理。
#define 允许您定义一个符号,这样,通过使用符号作为传递给 #if 指令的表达式,表达式将返回 true。
17.IO-输入和输出
为什么要出现与文件流配套的读写器类型呢?
主要是因为文件流对象(FileStream)在读写字节的效率是相当高的,但是在处理其他类型的数据时会比较麻烦,所以就出现了二进制读写器(BinaryReader和BinaryWriter)和文本读写器(StreamReader和StreamWriter)来解决这一问题。
18.委托和事件
区别1:委托是一个类型,事件修饰的是一个对象
区别2:委托可以在声明它的类外部进行调用,而事件只能在类的内部进行调用。
19.异步回调
1.调用BeginInvoke开始异步加载, 在 ThreadPool 线程上执行回调方法,而不是在主线程。
2.调用 EndInvoke 来完成异步调用
20.序列化和反序列化
序列化是指将对象转换成字节流,从而存储对象或将对象传输到内存、数据库或文件的过程。主要用途是保存对象的状态,以便可以在需要的时候重新创建对象。反向过程就是反序列化。
21.反射
反射是程序可以访问、检测、修改它本身状态或行为一种能力。
程序集包括模块,模块包括类型,类型又包括成员。反射可以提供封装程序集、模块和类型的对象。
反射可以动态的创建类型的实例,把类型绑定在现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
22.特性
特性:描述运行时传递程序中的各种元素(类、方法、结构体等)行为信息的声明性标签。
一个特性,是放置在它应用元素的前面的方括号来描述的。
预定义特性,AttributeUsage是来描述如何使用一个预定义特性类,第一个参数表示可以使用的语言元素,第二个参数表示多用的。
23.多线程
- Thread:自己创建的独立的线程, 优先级高,需要使用者自己管理。
- ThreadPool:.Net 自己管理, 只需要把需要处理的方法写好, 然后交个.Net Framework, 后续只要方法执行完毕, 则自动退出。
- Task:C#4.0 以后新增的线程操作方式, 类似 ThreadPool, 但效率测试比ThreadPool略高, Task对多核的支持更为明显,所以在多核的处理器中, Task的优势更为明显。
class Program
{
static void Main(string[] args)
{ //独立创建线程
Thread t = new Thread(ThreadProcess);
t.Start(new object());
//线程池
ThreadPool.QueueUserWorkItem(ThreadProcess, new object());
//Task方式创建线程
System.Threading.Tasks.Task.Factory.StartNew(ThreadProcess, new object());
//需要手动终止,当然现在终止可能线程还未运行完成,
t.Abort();
}
private static void ThreadProcess(object tag)
{
int i = 100;
while (i > 0)
{
Console.WriteLine(string.Format("i:{0} ", i));
Thread.Sleep(10);
i--;
}
}
}