协变逆变 多线程 预处理指令
文章目录
1、协变逆变
1、协变逆变概念
协变:
和谐自然的变化
里氏替换原则,父类泛型委托装子类泛型委托
所以子类变父类,比如string变object感受是和谐自然的
逆变:
子类泛型委托装父类泛型委托
父类变子类,比如Object变string,感受是不和谐的
协变和逆变是用来修饰泛型的
协变:out
逆变:in
用于在泛型中修饰泛型字母的
只有泛型接口和泛型委托才能使用
2、作用
1、返回值和参数
用out修饰的泛型类型,只能作为返回值类型
delegate T TestOut<out T>();
interface Test<out T>{ T TestFun();}
用in修饰的泛型类型,只能作为参数类型
delegate void TestIn<in T>(T t);
2、结合里氏替换原则理解
用out和in修饰的泛型委托,可以相互装载(有继承关系的泛型)
//协变 父类总是能被子类替换
TestOut<Son> os = () =>
{
return new Son();
};
//用out修饰,可自动判断返回值有无继承关系
TestOut<Father> of = os;
//看起来调用了父类,实际上代码通过协变,返回的是os里面的函数,返回的是Son
Father f = of();
//逆变 父类总是能被子类替换
TestIn<Father> iF = (value) =>{};
TestIn<Son> iS = iF;
iS(new Son());
delegate T TestOut<out T>();
delegate void TestIn<in T>(T t);
class Father { }
class Son : Father { }
2、多线程
1、进程
运行应用程序就是在操作系统上开启了一个进程
编程可以理解为制作进程
2、线程概念
线程:
代码依次执行的一条路径
进程可以并发多个线程
在线程里面编程
3、多线程概念
同时运行代码的多条线程
4、语法
线程类 Thread
1、声明一个新的线程
线程执行的代码,需要封装到一个函数中
//声明线程
Thread t = new Thread(NewThreadLogic);
2、启动线程
t.Start();
3、设置为后台线程
//若不设置后台线程,可能导致进程无法正常关闭
t.IsBackground = true;
void NewThreadLogic()
{
Console.WriteLine("新开的代码逻辑");
}
4、关闭释放一个线程
1、加bool标识
2、通过线程提供的方法(在.Net core版本无法中止)
t.Abort();
t = null;
5、线程休眠
//单位毫秒 1s = 1000毫秒
Thread.Sleep(1000);
5、线程之间共享数据
多个线程使用的内存是共享的,都属于该应用程序(进程)
当多线程同时操作同一片内存区域时可能会出问题
可以通过加锁的形式避免问题
lock(obj){}
6、多线程的意义
可以用多线程专门处理一些复杂耗时的逻辑
比如:寻路、网络通信等
思考 多线程移动方块
//控制台有一个■,移动。用多线程来检测输入,控制转向
//移动的方向。位置、绘制、擦除、转向
Console.CursorVisible = false;
Icon i = new Icon(10, 5, E_MoveDir.Right);
i.Drow();
Thread t = new Thread(NewThreadLogic);
t.Start();
while (true)
{
Thread.Sleep(300);
i.Clear();
i.Move();
i.Drow();
}
void NewThreadLogic()
{
while (true)
{
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.W:
i.ChangeDir(E_MoveDir.Up);
break;
case ConsoleKey.S:
i.ChangeDir(E_MoveDir.Down);
break;
case ConsoleKey.A:
i.ChangeDir(E_MoveDir.Left);
break;
case ConsoleKey.D:
i.ChangeDir(E_MoveDir.Right);
break;
}
}
}
enum E_MoveDir
{
Up,
Down,
Left,
Right,
}
class Icon
{
//当前移动方向
public E_MoveDir dir;
public int x;
public int y;
public Icon(int x, int y,E_MoveDir dir)
{
this.x = x;
this.y = y;
this.dir = dir;
}
public void Move()
{
switch (dir)
{
case E_MoveDir.Up:
y--;
break;
case E_MoveDir.Down:
y++;
break;
case E_MoveDir.Left:
x -= 2;
break;
case E_MoveDir.Right:
x += 2;
break;
default:
break;
}
}
public void Drow()
{
Console.SetCursorPosition(x, y);
Console.Write("■");
}
public void Clear()
{
Console.SetCursorPosition(x, y);
Console.Write(" ");
}
public void ChangeDir(E_MoveDir dir)
{
this.dir = dir;
}
}
3、预处理指令
1、编译器概念
编译器是一种翻译程序
它用于将源语言程序翻译为目标语言程序
源语言程序:
某种程序设计语言写成,比如C#、c、c++、Java
目标语言程序:
二进制数表示的伪机器代码写的程序
2、预处理器指令
预处理器指令:
指导编译器,在实际编译开始之前对信息进行预处理
预处理器指令都是以#开始
3、常见的预处理器指令
1
#define
定义一个符号,类似一个没有值的变量
一般配合if、特性使用
#undef
2、
#if
#elif
#else
#endif
和if语句规则一样,一般配合#define定义的符号使用
用于告诉编译器进行编译代码的流程控制
#define Unity4
#define Unity2017
#define IOS
//#undef Unity4
#if Unity4
Console.WriteLine("版本为Unity4");
#elif Unity2017 && IOS
Console.WriteLine("版本为Unity2017");
#else
Console.WriteLine("其他版本");
#endif
3
#warning
#error
总结
预处理指令
可以让代码还没有编译之前就可以进行一些预处理判断
在Unity中会用来进行一些平台或者版本的判断
决定不同的版本或者不同的平台使用不同的逻辑代码
思考 预处理器指令实现
//请使用预处理器指令实现
//写一个函数计算两个数
//当是Unity5版本时算加法
//当是Unity2017版本时算乘法
//当是Unity2020版本时算减法
//#define Unity5
#define Unity2017
#define Unity2020
Console.WriteLine(Calc(1,2));
int Calc(int a,int b)
{
#if Unity5
return a+b;
#elif Unity2017
return a*b;
#elif Unity2020
return a-b;
#else
return 0;
#endif
}