C#复习笔记(七)
协变逆变(out、in)
用来修饰泛型字母,只有泛型接口和泛型委托能使用
用out修饰的泛型只能作为返回值
delegate T TestOut<out T>();
用in修饰的泛型只能作为参数
delegate void TestIn<in T>(T t);
class Father{}
class Son : Father{}
协变:父类泛型委托装子类泛型委托
TestOut<Son> os = () =>{ return new Son()};
TestOut<Father> of = os; //加了out后会判断返回值有没有父子关系
Father f = of(); //Father存of执行后的返回值
逆变:子类泛型委托装父类泛型委托
TestIn<Father> iF = (value) =>{};
TestIn<Son> iS = iF;
iS(new Son());
多线程 Thread
进程(Process)是计算机程序关于某数据集合上的一次运行活动
是系统进行资源分配和调度的基本单位,是操作系统结构的基础
进程之间可以相互独立运行互不干扰,也可以相互访问、操作
线程
操作系统能够进行运算调度的最小单位
被包含在进程中,是进程中的实际运作单位
一条线程指的是进程中的一个顺序控制流,一个进程中可以并发多个线程
语法
线程的执行代码需要封装到一个函数中
Thread t = new Thread(NewThread); //新线程将要执行的代码逻辑被封装到了一个函数语句块中
static void NewThread()
{
while(true){}
};
线程,启动!
t.Start();
后台进程
随着前台线程的结束而结束
不设置后台线程会导致进程无法正常关闭
t.IsBackground = true;
关闭释放线程
如果开启的线程中不是死循环,是能结束逻辑的就不用刻意去关闭它
有两种方式中止线程:
1、设置bool值为false跳出while循环
2、t.Abort(); //线程自带的Abort()方法,但在.Net core版本中无法中止会报错
线程休眠
t.Sleep(100); //毫秒为单位
线程间的共享数据
多个线程使用的内存是共享的,都属于该应用程序(进程)
同时操作通一片内存区域时可能会出问题,可以通过加锁(lock)的形式避免问题
加锁 lock(引用类型对象)
当我们在多个线程访问相同的东西进行逻辑处理时
为了避免不必要的逻辑顺序执行差错
lock中对象被锁住会等待被锁住的对象解锁,只有解锁了才运行逻辑
预处理器指令
编译器
编译器是一种翻译程序,用于将源语言程序(某种程序设计语言写成,比如C#、C++、Java等语言写的程序)翻译为目标语言程序(二进制数表示的伪机器代码写的程序)
预处理指令
指导编译器在实际编译开始前对信息进行预处理
以#开头,预处理指令不是语句、所以不以分号结束
常见的预处理器指令
#define //定义一个符号
#undef //取消定义的符号
上述两者写在脚本文件最上方,一般配合 #if 指令使用或配合特性
#if
#elif
#else
#endif
和if语句规则一样
#warning //警告
#error //报错
反射
程序正在运行时可以查看其它程序集或者自身的元数据
一个运行的程序查看本身或者其他程序的元数据的行为就叫做反射
程序集
程序集是经由编译器编译得到的供进一步编译执行的中间产物
在Windows系统中一般是后缀为.dll的库文件或.exe的可执行文件
元数据:用来描述数据的数据
反射的作用
可以在程序编译后获得信息,提高了程序的拓展性和灵活性
1、程序运行时得到所有元数据,包括元数据的特性
2、程序运行时实例化对象、操作对象
3、程序运行时创建新对象,用这些对象执行任务
语法
Type(类的信息类)
反射功能的基础,访问元数据的主要方式
GetType() 可以获取对象的Type
GetType("System.Name") 用类名也可以,但必须包含命名空间
typeof() 传入类名,也可得到对象的Type
获取类中所有公共成员
Type t = typeof(Test);
MemberInfo[] info = t.GetMembers(); //使用了反射
获取类的公共构造函数并调用
ConstructorInfo[] c = t.GetConstructors(); //获取所有构造函数
1、得到无参构造
ConstructorInfo info = t.GetConstructor(new Type[0]);
info.Invoke(null);
Test obj = info.Invoke(null) as Test; //可以使用对象装起来
2、得到有参构造
ConstructorInfo info2 = t.GetConstructor(new Type[] {typeof(int)}); //括号中代表的是int类型的构造
Test obj = info2.Invoke(new object[] {2}) as Test; // 把 i 赋值为2
获取类的公共成员变量
1、得到所有成员变量
FieldInfo[] info = t.GetFields();
2、得到指定名称公共成员变量
FieldInfo infoGet = t.GetField("i");
3、通过反射获取和设置对象的值
Test test = new Test();
infoGet.GetValue(test); //获取对象的某个变量的值
infoGet.SetValue(test , 114514);
获取类中的成员方法
使用Type类中的GetMethod方法得到类中的方法
Type s = typeof(string); //获得string的元数据
1、如果存在方法重载,用Type数组表示参数类型
MethodInfo[] m = s.GetMethods(); //得到string类型的所有方法
MethodeInfo subStr = s.GetMethod("Substring" , new Type[] {typeof(int) , typeof(int)});
2、调用方法
string str = "Hello,World";
subStr.Invoke(str , new object[] { 6 , 5 });
补充
Type内还有得枚举、得事件、得接口、得属性等
GetEnumName、GetEvent、GetInterface、GetProperty
Activator
用于快速实例化对象的类,可将Type对象快捷实例化为对象
先得到Type
Type tp = typeof(Test);
1、无参构造
Test obj = Activator.CreateInstance(tp) as Test;
2、无参构造
obj = Activator.CreateInstance(tp , 99) as Test; //调用一个参数的构造
创建的参数一定要和构造函数的参数相同
Assembly 程序集类
用来加载其他程序集,加载后才用Type使用其他程序集的信息
如果想要使用不是自己程序集的内容,就需要先加载程序集
加载程序集
用于加载同一文件夹下的其他程序集
Assembly.Load("程序集名称");
Assembly.LoadFrom("包含程序集清单的文件名称或路径");
Assembly.LoadFile();
1、加载指定程序集
2、再加载程序集中的一个类对象,之后使用反射
3、类库创建
特性 Attribute
是一种允许我们向程序集添加元数据的语言结构,用于保存程序结构信息的某种特殊类型的类
比如一个类、成员变量、成员方法等,为它们添加更多的额外信息
需要继承特性基类使用
语法:
写在类、函数、变量上一行,表示它们具有该特性信息
[ 特性名(参数列表) ]
本质上就是在调用特性类的构造函数
class MyCustom : Attribute
{
public string info;
public MyCustom(string info)
{
this.info = info;
}
}
特性的使用
如果特性名在申明时名字中含有Attribute,系统会在调用时自动去除这段字符,使用会方便点
[MyCustom("我是一个特性")]
class Test{}
Test t = new test();
三种获取Type的方式
Type x = t.GetType();
x = typeof(Test);
x = Type.GetType("命名空间");
判断是否使用了某个特性
IsDefined( 参数的类型 ,是否搜索继承链(寻找父类中的特性,属性和事件忽略此参数))
if( x.IsDefined(typeof(MyCustom), false) ){}
获取Type元数据中所有的特性
object[] array = x.GetCustomAttributes()
限制自定义特性的使用范围
由系统提供
[AttributeUsage(AttributeTargets.class(能够用在哪些地方,加.class就是类),
AllowMultiple = true(是否允许多个特性实例用在同一个目标上),
Inherited = true (特性是否能被派生类和重写成员继承) )]
调用者信息特性
CallerFilePath
CallerLineNumber
CallerMemberName
条件编译特性 Conditional
配合 #define使用
主要用在调试代码上
外部DLL包函数特性 DllImport
[DllImport("路径名或文件名")]
public static extern int Add(int a, int b) //extern关键字用于声明方法的外部实现
迭代器 iterator
提供一个方法顺序访问一个聚合对象中的各个元素
而又不暴露其内部的标识
实现方法
继承接口:IEnumerator,IEnumerable
class CustomList : IEnumerator , IEnumerable
{}
foreach(int item in list){}
foreach本质:
1、先获取in后面这个对象的IEnumerator,会调用对象其中的GetEnumeratorf获取
2、执行得到这个IEnumerator对象中的MoveNext方法
3、只要MoveNext方法的返回值是 true,就会去获取Current并复制给item
IEnumerable中的 yield return 语法糖
public IEnumerable GetEnumerator()
{
for(int i = 0 ; i < list.Length;i++)
{ yield return list[i]; } //暂时返回保留当前状态
}