多线程
using System;
using System.Threading;namespace Lesson18_多线程
{
class Program
{
static bool isRuning = true;static object obj = new object();
static void Main(string[] args)
{
Console.WriteLine("多线程");
知识点一 了解线程前先了解进程
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动
是系统进行资源分配和调度的基本单位,是操作系统结构的基础
说人话:打开一个应用程序就是在操作系统上开启了一个进程
进程之间可以相互独立运行,互不干扰
进程之间也可以相互访问、操作知识点二 什么是线程
操作系统能够进行运算调度的最小单位。
它被包含在进程之中,是进程中的实际运作单位
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程
我们目前写的程序 都在主线程中简单理解线程:
就是代码从上到下运行的一条“管道”知识点三 什么是多线程
我们可以通过代码 开启新的线程
可以同时运行代码的多条“管道” 就叫多线程知识点四 语法相关
线程类 Thread
需要引用命名空间 using System.Threading;1.申明一个新的线程
注意 线程执行的代码 需要封装到一个函数中
新线程 将要执行的代码逻辑 被封装到了一个函数语句块中
Thread t = new Thread(NewThreadLogic);2.启动线程
t.Start();
3.设置为后台线程
当前台线程都结束了的时候,整个程序也就结束了,即使还有后台线程正在运行
后台线程不会防止应用程序的进程被终止掉
如果不设置为后台线程 可能导致进程无法正常关闭
t.IsBackground = true;
4.关闭释放一个线程
如果开启的线程中不是死循环 是能够结束的逻辑 那么 不用刻意的去关闭它
如果是死循环 想要中止这个线程 有两种方式4.1-死循环中bool标识
Console.ReadKey();isRuning = false;
Console.ReadKey();
4.2-通过线程提供的方法(注意在.Net core版本中无法中止 会报错)
中止线程
try
{
t.Abort();
t = null;
}
catch
{}
5.线程休眠
让线程休眠多少毫秒 1s = 1000毫秒
在哪个线程里执行 就休眠哪个线程
Thread.Sleep(1000);知识点五 线程之间共享数据
多个线程使用的内存是共享的,都属于该应用程序(进程)
所以要注意 当多线程 同时操作同一片内存区域时可能会出问题
可以通过加锁的形式避免问题lock
当我们在多个线程当中想要访问同样的东西 进行逻辑处理时
为了避免不必要的逻辑顺序执行的差错
lock(引用类型对象)while(true)
{
lock(obj)
{
Console.SetCursorPosition(0, 0);
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("●");
}
}知识点六 多线程对于我们的意义
可以用多线程专门处理一些复杂耗时的逻辑
比如 寻路、网络通信等等static void NewThreadLogic()
{
新开线程 执行的代码逻辑 在该函数语句块中
while(isRuning)
{
//Thread.Sleep(1000);
//Console.WriteLine("新开线程代码逻辑");
lock(obj)
{
Console.SetCursorPosition(10, 5);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("■");
}
}
}
}总结
多线程是多个可以同时执行代码逻辑的“管道”
可以通过代码开启多线程,用多线程处理一些复杂的可能影响主线程流畅度的逻辑
关键字 Thread
预处理指令
//定义一个符号
#define Unity4
#define Unity5
#define Unity2017
#define Unity2019
//取消定义一个符号
#undef Unity4#define IOS
#define Android
#define PC
知识点一 什么是编译器
编译器是一种翻译程序
它用于将源语言程序翻译为目标语言程序源语言程序:某种程序设计语言写成的,比如C#、C、C++、Java等语言写的程序
目标语言程序:二进制数表示的伪机器代码写的程序知识点二 什么是预处理器指令
预处理器指令 指导编译器 在实际编译开始之前对信息进行预处理
预处理器指令 都是以#开始
预处理器指令不是语句,所以它们不以分号;结束
目前我们经常用到的 折叠代码块 就是预处理器指令知识点三 常见的预处理器指令
1
#define
定义一个符号,类似一个没有值的变量
#undef
取消define定义的符号,让其失效
两者都是写在脚本文件最前面
一般配合 if指令使用 或配合特性
2
#if
#elif
#else
#endif
和if语句规则一样,一般配合#define定义的符号使用
用于告诉编译器进行编译代码的流程控制如果发现有Unity4这个符号 那么其中包含的代码 就会被编译器翻译
可以通过 逻辑或 和 逻辑与 进行多种符号的组合判断#if Unity4Console.WriteLine("版本为Unity4"); #elif Unity2017 && IOSConsole.WriteLine("版本为Unity2017");#warning 这个版本 不合法#error 这个版本不准执行 #elseConsole.WriteLine("其它版本"); #endif
3
#warning
#error
告诉编译器
是报警告还是报错误
一般还是配合if使用总结
预处理器指令
可以让代码还没有编译之前就可以进行一些预处理判断
在Unity中会用来进行一些平台或者版本的判断
决定不同的版本或者不同的平台使用不同的代码逻辑
反射和特性
using System;
using System.Reflection;
using System.Threading;namespace Lesson20_反射
{
#region 知识点回顾
//编译器是一种翻译程序
//它用于将源语言程序翻译为目标语言程序//源语言程序:某种程序设计语言写成的,比如C#、C、C++、Java等语言写的程序
//目标语言程序:二进制数表示的伪机器代码写的程序
#endregion#region 知识点一 什么是程序集
//程序集是经由编译器编译得到的,供进一步编译执行的那个中间产物
//在WINDOWS系统中,它一般表现为后缀为·dll(库文件)或者是·exe(可执行文件)的格式//说人话:
//程序集就是我们写的一个代码集合,我们现在写的所有代码
//最终都会被编译器翻译为一个程序集供别人使用
//比如一个代码库文件(dll)或者一个可执行文件(exe)
#endregion#region 知识点二 元数据
//元数据就是用来描述数据的数据
//这个概念不仅仅用于程序上,在别的领域也有元数据//说人话:
//程序中的类,类中的函数、变量等等信息就是 程序的 元数据
//有关程序以及类型的数据被称为 元数据,它们保存在程序集中
#endregion#region 知识点三 反射的概念
//程序正在运行时,可以查看其它程序集或者自身的元数据。
//一个运行的程序查看本身或者其它程序的元数据的行为就叫做反射//说人话:
//在程序运行时,通过反射可以得到其它程序集或者自己程序集代码的各种信息
//类,函数,变量,对象等等,实例化它们,执行它们,操作它们
#endregion#region 知识点四 反射的作用
//因为反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性
//1.程序运行时得到所有元数据,包括元数据的特性
//2.程序运行时,实例化对象,操作对象
//3.程序运行时创建新对象,用这些对象执行任务
#endregionclass Test
{
private int i = 1;
public int j = 0;
public string str = "123";
public Test()
{}
public Test(int i)
{
this.i = i;
}public Test( int i, string str ):this(i)
{
this.str = str;}
public void Speak()
{
Console.WriteLine(i);
}
}class Program
{
static void Main(string[] args)
{
Console.WriteLine("反射");
#region 知识点五 语法相关#region Type
//Type(类的信息类)
//它是反射功能的基础!
//它是访问元数据的主要方式。
//使用 Type 的成员获取有关类型声明的信息
//有关类型的成员(如构造函数、方法、字段、属性和类的事件)#region 获取Type
//1.万物之父object中的 GetType()可以获取对象的Type
int a = 42;
Type type = a.GetType();
Console.WriteLine(type);
//2.通过typeof关键字 传入类名 也可以得到对象的Type
Type type2 = typeof(int);
Console.WriteLine(type2);
//3.通过类的名字 也可以获取类型
// 注意 类名必须包含命名空间 不然找不到
Type type3 = Type.GetType("System.Int32");
Console.WriteLine(type3);#endregion
#region 得到类的程序集信息
//可以通过Type可以得到类型所在程序集信息
Console.WriteLine(type.Assembly);
Console.WriteLine(type2.Assembly);
Console.WriteLine(type3.Assembly);
#endregion#region 获取类中的所有公共成员
//首先得到Type
Type t = typeof(Test);
//然后得到所有公共成员
//需要引用命名空间 using System.Reflection;
MemberInfo[] infos = t.GetMembers();
for (int i = 0; i < infos.Length; i++)
{
Console.WriteLine(infos[i]);
}
#endregion#region 获取类的公共构造函数并调用
//1.获取所有构造函数
ConstructorInfo[] ctors = t.GetConstructors();
for (int i = 0; i < ctors.Length; i++)
{
Console.WriteLine(ctors[i]);
}//2.获取其中一个构造函数 并执行
//得构造函数传入 Type数组 数组中内容按顺序是参数类型
//执行构造函数传入 object数组 表示按顺序传入的参数
// 2-1得到无参构造
ConstructorInfo info = t.GetConstructor(new Type[0]);
//执行无参构造 无参构造 没有参数 传null
Test obj = info.Invoke(null) as Test;
Console.WriteLine(obj.j);// 2-2得到有参构造
ConstructorInfo info2 = t.GetConstructor(new Type[] { typeof(int) });
obj = info2.Invoke(new object[] { 2 }) as Test;
Console.WriteLine(obj.str);ConstructorInfo info3 = t.GetConstructor(new Type[] { typeof(int), typeof(string) });
obj = info3.Invoke(new object[] { 4, "444444" }) as Test;
Console.WriteLine(obj.str);
#endregion#region 获取类的公共成员变量
//1.得到所有成员变量
FieldInfo[] fieldInfos = t.GetFields();
for (int i = 0; i < fieldInfos.Length; i++)
{
Console.WriteLine(fieldInfos[i]);
}
//2.得到指定名称的公共成员变量
FieldInfo infoJ = t.GetField("j");
Console.WriteLine(infoJ);//3.通过反射获取和设置对象的值
Test test = new Test();
test.j = 99;
test.str = "2222";
// 3-1通过反射 获取对象的某个变量的值
Console.WriteLine(infoJ.GetValue(test));
// 3-2通过反射 设置指定对象的某个变量的值
infoJ.SetValue(test, 100);
Console.WriteLine(infoJ.GetValue(test));
#endregion#region 获取类的公共成员方法
//通过Type类中的 GetMethod方法 得到类中的方法
//MethodInfo 是方法的反射信息
Type strType = typeof(string);
MethodInfo[] methods = strType.GetMethods();
for (int i = 0; i < methods.Length; i++)
{
Console.WriteLine(methods[i]);
}
//1.如果存在方法重载 用Type数组表示参数类型
MethodInfo subStr = strType.GetMethod("Substring",
new Type[] { typeof(int), typeof(int) });
//2.调用该方法
//注意:如果是静态方法 Invoke中的第一个参数传null即可
string str = "Hello,World!";
//第一个参数 相当于 是哪个对象要执行这个成员方法
object result = subStr.Invoke(str, new object[] { 7, 5 });
Console.WriteLine(result);#endregion
#region 其它
//Type;
//得枚举
//GetEnumName
//GetEnumNames//得事件
//GetEvent
//GetEvents//得接口
//GetInterface
//GetInterfaces//得属性
//GetProperty
//GetPropertys
//等等
#endregion#endregion
#region Assembly
//程序集类
//主要用来加载其它程序集,加载后
//才能用Type来使用其它程序集中的信息
//如果想要使用不是自己程序集中的内容 需要先加载程序集
//比如 dll文件(库文件)
//简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类//三种加载程序集的函数
//一般用来加载在同一文件下的其它程序集
//Assembly asembly2 = Assembly.Load("程序集名称");//一般用来加载不在同一文件下的其它程序集
//Assembly asembly = Assembly.LoadFrom("包含程序集清单的文件的名称或路径");
//Assembly asembly3 = Assembly.LoadFile("要加载的文件的完全限定路径");//1.先加载一个指定程序集
Assembly asembly = Assembly.LoadFrom(@"C:\Users\MECHREVO\Desktop\CSharp进阶教学\Lesson18_练习题\bin\Debug\netcoreapp3.1\Lesson18_练习题");
Type[] types = asembly.GetTypes();
for (int i = 0; i < types.Length; i++)
{
Console.WriteLine(types[i]);
}
//2.再加载程序集中的一个类对象 之后才能使用反射
Type icon = asembly.GetType("Lesson18_练习题.Icon");
MemberInfo[] members = icon.GetMembers();
for (int i = 0; i < members.Length; i++)
{
Console.WriteLine(members[i]);
}
//通过反射 实例化一个 icon对象
//首先得到枚举Type 来得到可以传入的参数
Type moveDir = asembly.GetType("Lesson18_练习题.E_MoveDir");
FieldInfo right = moveDir.GetField("Right");
//直接实例化对象
object iconObj = Activator.CreateInstance(icon, 10, 5, right.GetValue(null));
//得到对象中的方法 通过反射
MethodInfo move = icon.GetMethod("Move");
MethodInfo draw = icon.GetMethod("Draw");
MethodInfo clear = icon.GetMethod("Clear");Console.Clear();
while(true)
{
Thread.Sleep(1000);
clear.Invoke(iconObj, null);
move.Invoke(iconObj, null);
draw.Invoke(iconObj, null);
}//3.类库工程创建
#endregion#region Activator
//用于快速实例化对象的类
//用于将Type对象快捷实例化为对象
//先得到Type
//然后 快速实例化一个对象
Type testType = typeof(Test);
//1.无参构造
Test testObj = Activator.CreateInstance(testType) as Test;
Console.WriteLine(testObj.str);
//2.有参数构造
testObj = Activator.CreateInstance(testType, 99) as Test;
Console.WriteLine(testObj.j);testObj = Activator.CreateInstance(testType, 55, "111222") as Test;
Console.WriteLine(testObj.j);
#endregion#endregion
}
}
总结
反射:在程序运行时,通过反射可以得到其他程序集或者自己的程序集代码的各种信息
类、函数、变量、对象等等,实例化他们,执行他们,操作他们关键类
Type
Assembly
Activator对于我们的意义
为什么要学反射
为了之后学习Unity引擎的基本工作原理做铺垫
Unity引起的基本工作机制 就是建立在反射的基础上
}
特性
#define Fun
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;namespace Lesson21_特性
{
#region 知识点一 特性是什么
//特性是一种允许我们向程序的程序集添加元数据的语言结构
//它是用于保存程序结构信息的某种特殊类型的类//特性提供功能强大的方法以将声明信息与 C# 代码(类型、方法、属性等)相关联。
//特性与程序实体关联后,即可在运行时使用反射查询特性信息//特性的目的是告诉编译器把程序结构的某组元数据嵌入程序集中
//它可以放置在几乎所有的声明中(类、变量、函数等等申明)//说人话:
//特性本质是个类
//我们可以利用特性类为元数据添加额外信息
//比如一个类、成员变量、成员方法等等为他们添加更多的额外信息
//之后可以通过反射来获取这些额外信息
#endregion#region 知识点二 自定义特性
//继承特性基类 Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field, AllowMultiple = true, Inherited = false)]
class MyCustomAttribute : Attribute
{
//特性中的成员 一般根据需求来写
public string info;public MyCustomAttribute(string info)
{
this.info = info;
}public void TestFun()
{
Console.WriteLine("特性的方法");
}
}
#endregion#region 知识点三 特性的使用
//基本语法:
//[特性名(参数列表)]
//本质上 就是在调用特性类的构造函数
//写在哪里?
//类、函数、变量上一行,表示他们具有该特性信息[MyCustom("这个是我自己写的一个用于计算的类")]
[MyCustom("这个是我自己写的一个用于计算的类")]
class MyClass
{
[MyCustom("这是一个成员变量")]
public int value;//[MyCustom("这是一个用于计算加法的函数")]
//public void TestFun( [MyCustom("函数参数")]int a )
//{//}
public void TestFun(int a)
{}
}
#endregion#region 知识点四 限制自定义特性的使用范围
//通过为特性类 加特性 限制其使用范围
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = true, Inherited = true)]
//参数一:AttributeTargets —— 特性能够用在哪些地方
//参数二:AllowMultiple —— 是否允许多个特性实例用在同一个目标上
//参数三:Inherited —— 特性是否能被派生类和重写成员继承
public class MyCustom2Attribute : Attribute
{}
#endregion#region 知识点五 系统自带特性——过时特性
//过时特性
//Obsolete
//用于提示用户 使用的方法等成员已经过时 建议使用新方法
//一般加在函数前的特性class TestClass
{
//参数一:调用过时方法时 提示的内容
//参数二:true-使用该方法时会报错 false-使用该方法时直接警告
[Obsolete("OldSpeak方法已经过时了,请使用Speak方法", false)]
public void OldSpeak(string str)
{
Console.WriteLine(str);
}public void Speak()
{}
public void SpeakCaller(string str, [CallerFilePath]string fileName = "",
[CallerLineNumber]int line = 0, [CallerMemberName]string target = "")
{
Console.WriteLine(str);
Console.WriteLine(fileName);
Console.WriteLine(line);
Console.WriteLine(target);
}
}#endregion
#region 知识点六 系统自带特性——调用者信息特性
//哪个文件调用?
//CallerFilePath特性
//哪一行调用?
//CallerLineNumber特性
//哪个函数调用?
//CallerMemberName特性//需要引用命名空间 using System.Runtime.CompilerServices;
//一般作为函数参数的特性
#endregion#region 知识点七 系统自带特性——条件编译特性
//条件编译特性
//Conditional
//它会和预处理指令 #define配合使用//需要引用命名空间using System.Diagnostics;
//主要可以用在一些调试代码上
//有时想执行有时不想执行的代码
#endregion#region 知识点八 系统自带特性——外部Dll包函数特性
//DllImport//用来标记非.Net(C#)的函数,表明该函数在一个外部的DLL中定义。
//一般用来调用 C或者C++的Dll包写好的方法
//需要引用命名空间 using System.Runtime.InteropServices
#endregion
class Program
{
[DllImport("Test.dll")]
public static extern int Add(int a, int b);[Conditional("Fun")]
static void Fun()
{
Console.WriteLine("Fun执行");
}static void Main(string[] args)
{
Console.WriteLine("特性");#region 特性的使用
MyClass mc = new MyClass();
Type t = mc.GetType();
//t = typeof(MyClass);
//t = Type.GetType("Lesson21_特性.MyClass");//判断是否使用了某个特性
//参数一:特性的类型
//参数二:代表是否搜索继承链(属性和事件忽略此参数)
if( t.IsDefined(typeof(MyCustomAttribute), false) )
{
Console.WriteLine("该类型应用了MyCustom特性");
}//获取Type元数据中的所有特性
object[] array = t.GetCustomAttributes(true);
for (int i = 0; i < array.Length; i++)
{
if( array[i] is MyCustomAttribute )
{
Console.WriteLine((array[i] as MyCustomAttribute).info);
(array[i] as MyCustomAttribute).TestFun();
}
}TestClass tc = new TestClass();
tc.OldSpeak("123");
tc.Speak();tc.SpeakCaller("123123123123123");
Fun();
#endregion
}
}
总结:
特性是用于 为元数据再添加更多的额外信息(变量、方法等等)
我们可以通过反射获取这些额外的数据 来进行一些特殊的处理
自定义特性——继承Attribute类系统自带特性:过时特性
为什么要学习特性
Unity引擎中很多地方都用到了特性来进行一些特殊处理