- 一、ArrayList
- 二、Stack
- 三、Queue
- 四、Hashtable
- 五、泛型
- 六、泛型约束
- 七、List
- 八、Dictionary
- 九、LinkedList
- 十、泛型 Stack 和 Queue
- 十一、委托
- 十二、事件
- 十三、匿名函数
- 十四、Lambda表达式
- 十五、闭包
- 十六、List 排序
- 十七、协变逆变
- 十八、多线程
- 十九、预处理器指令
- 二十、反射
- 二十一、特性
- 二十二、迭代器
- 二十三、特殊语法
// ArrayList的本质
// ArrayList是一个C#为我们封装好的类,
// 它的本质是一个object类型的数组,
// ArrayList类帮助我们实现了很多方法,
// 比如数组的增删查改
// 申明
// 需要引用命名空间 using System.Collections;
var array = new ArrayList();
// 增删查改
// 增
array.Add(1);
array.Add("123");
array.Add(true);
array.Add(new object());
array.Add(new Test());
array.Add(1);
array.Add(true);
var array2 = new ArrayList();
array2.Add(123);
// 范围增加(批量增加 把另一个list容器里面的内容加到后面)
array.AddRange(array2);
array.Insert(1, "12345676");
Console.WriteLine(array[1]);
// 删
// 移除指定元素 从头找 找到删
array.Remove(1);
// 移除指定位置的元素
array.RemoveAt(2);
// 清空
// array.Clear();
// 查
// 得到指定位置的元素
Console.WriteLine(array[0]);
// 查看元素是否存在
if (array.Contains("1234")) Console.WriteLine("存在123");
// 正向查找元素位置
// 找到的返回值 是位置 找不到 返回值 是-1
var index = array.IndexOf(true);
Console.WriteLine(index);
Console.WriteLine(array.IndexOf(false));
// 反向查找元素位置
// 返回时从头开始的索引数
index = array.LastIndexOf(true);
Console.WriteLine(index);
// 改
Console.WriteLine(array[0]);
array[0] = "999";
Console.WriteLine(array[0]);
// 遍历
// 长度
Console.WriteLine(array.Count);
// 容量
// 避免产生过多的垃圾
Console.WriteLine(array.Capacity);
for (var i = 0; i throw new NotImplementedException();
set => throw new NotImplementedException();
}
}
// 泛型方法
// 1.普通类中的泛型方法
internal class Test2
{
public void TestFun(T value)
{
Console.WriteLine(value);
}
public void TestFun()
{
// 用泛型类型 在里面做一些逻辑处理
var t = default(T);
}
public T TestFun(string v)
{
return default;
}
public void TestFun(T t, K k, M m) { }
}
// 2.泛型类中的泛型方法
internal class Test2
{
public T value;
public void TestFun(K k)
{
Console.WriteLine(k);
}
// 这个不叫泛型方法 因为 T是泛型类申明的时候 就指定 在使用这个函数的时候
// 我们不能再去动态的变化了
public void TestFun(T t) { }
}
// 泛型的作用
// 1.不同类型对象的相同逻辑处理就可以选择泛型
// 2.使用泛型可以一定程度避免装箱拆箱
// 举例:优化ArrayList
internal class ArrayList
{
private T[] array;
public void Add(T value) { }
public void Remove(T value) { }
}
// 总结
// 1.申明泛型时 它只是一个类型的占位符
// 2.泛型真正起作用的时候 是在使用它的时候
// 3.泛型占位字母可以有n个用逗号分开
// 4.泛型占位字母一般是大写字母
// 5.不确定泛型类型时 获取默认值 可以使用default(占位字符)
// 6.看到包裹的字母 那肯定是泛型
六、泛型约束
让泛型的类型有一定的限制 关键字:where 泛型约束一共有6种
- 值类型 where 泛型字母:struct
- 引用类型 where 泛型字母:class
- 存在无参公共构造函数 where 泛型字母:new()
- 某个类本身或者其派生类 where 泛型字母:类名
- 某个接口的派生类型 where 泛型字母:接口名
- 另一个泛型类型本身或者派生类型 where 泛型字母:另一个泛型字母
where 泛型字母:(约束的类型)
// 各泛型约束讲解
// 值类型约束
internal class Test1 where T : struct
{
public T value;
public void TestFun(K v) where K : struct { }
}
// 引用类型约束
internal class Test2 where T : class
{
public T value;
public void TestFun(K k) where K : class { }
}
// 公共无参构造约束
internal class Test3 where T : new()
{
public T value;
public void TestFun(K k) where K : new() { }
}
internal class Test1 { }
internal class Test2
{
public Test2(int a) { }
}
// 类约束
internal class Test4 where T : Test1
{
public T value;
public void TestFun(K k) where K : Test1 { }
}
internal class Test3 : Test1 { }
// 接口约束
internal interface IFly { }
internal interface IMove : IFly { }
internal class Test4 : IFly { }
internal class Test5 where T : IFly
{
public T value;
public void TestFun(K k) where K : IFly { }
}
// 另一个泛型约束
internal class Test6 where T : U
{
public T value;
public void TestFun(K k) where K : V { }
}
// 约束的组合使用
internal class Test7 where T : class, new() { }
// 多个泛型有约束
internal class Test8 where T : class, new() where K : struct { }
七、List
// List的本质
// List是一个C#为我们封装好的类,
// 它的本质是一个可变类型的泛型数组,
// List类帮助我们实现了很多方法,
// 比如泛型数组的增删查改
// 申明
// 需要引用命名空间
// using System.Collections.Generic
var list = new List();
var list2 = new List();
var list3 = new List();
// 增删查改
// 增
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list2.Add("123");
var listStr = new List();
listStr.Add("123");
list2.AddRange(listStr);
list.Insert(0, 999);
Console.WriteLine(list[0]);
// 删
// 1.移除指定元素
list.Remove(1);
// 2.移除指定位置的元素
list.RemoveAt(0);
// 3.清空
list.Clear();
list.Add(1);
list.Add(2);
list.Add(3);
list.Add(4);
list.Add(1);
// 查
// 1.得到指定位置的元素
Console.WriteLine(list[0]);
// 2.查看元素是否存在
if (list.Contains(1)) Console.WriteLine("存在元素 1");
// 3.正向查找元素位置
// 找到返回位置 找不到 返回-1
var index = list.IndexOf(5);
Console.WriteLine(index);
// 4.反向查找元素位置
// 找到返回位置 找不到 返回-1
index = list.LastIndexOf(2);
Console.WriteLine(index);
// 改
Console.WriteLine(list[0]);
list[0] = 99;
Console.WriteLine(list[0]);
// 遍历
// 长度
Console.WriteLine(list.Count);
// 容量
// 避免产生垃圾
Console.WriteLine(list.Capacity);
for (var i = 0; i
// {
// //函数体
// };
// 使用
// 1.无参无返回
Action a = () => { Console.WriteLine("无参无返回值的lambad表达式"); };
a();
// 2.有参
Action a2 = value => { Console.WriteLine("有参数Lambad表达式{0}", value); };
a2(100);
// 3.甚至参数类型都可以省略 参数类型和委托或事件容器一致
Action a3 = value => { Console.WriteLine("省略参数类型的写法{0}", value); };
a3(200);
// 4.有返回值
Func a4 = value => {
Console.WriteLine("有返回值有参数的那么大表达式{0}", value);
return 1;
};
Console.WriteLine(a4("123123"));
// 其它传参使用等和匿名函数一样
// 缺点也是和匿名函数一样的
十五、闭包
// 闭包
// 内层的函数可以引用包含在它外层的函数的变量
// 即使外层函数的执行已经终止
// 注意:
// 该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。
internal class Test
{
public Test() {
int value = 10;
// 这里就形成了闭包
// 因为 当构造函数执行完毕时 其中申明的临时变量value的声明周期被改变了
action = () => { Console.WriteLine(value); };
for (int i = 0; i { Console.WriteLine(index); };
}
}
public event Action action;
public void DoSomthing() {
action();
}
}
十六、List 排序
系统自带的变量 (int,float,double,…) 一般都可以直接 Sort 自定义类 Sort 有两种方式
- 继承接口 IComparable
- 在 Sort 中传入委托函数
internal class Item : IComparable
{
public int money;
public Item(int money) {
this.money = money;
}
public int CompareTo(Item other) {
// 返回值的含义
// 小于0:
// 放在传入对象的前面
// 等于0:
// 保持当前的位置不变
// 大于0:
// 放在传入对象的后面
// 可以简单理解 传入对象的位置 就是0
// 如果你的返回为负数 就放在它的左边 也就前面
// 如果你返回正数 就放在它的右边 也就是后面
if (money > other.money)
return -1;
return 1;
}
}
internal class ShopItem
{
public int id;
public ShopItem(int id) {
this.id = id;
}
}
internal class Program
{
private static void Main(string[] args) {
Console.WriteLine("List排序");
// List自带排序方法
List list = new List();
list.Add(3);
list.Add(2);
list.Add(6);
list.Add(1);
list.Add(4);
list.Add(5);
for (int i = 0; i b.id ? 1 : -1; });
for (int i = 0; i b.id)
return -1;
return 1;
}
}
十七、协变逆变
协变: 和谐的变化,自然的变化 因为里氏替换原则,父类可以装子类,所以子类变父类
比如 string 变成 object,感受是和谐的
逆变: 逆常规的变化,不正常的变化 因为里氏替换原则 父类可以装子类 但是子类不能装父类,所以父类变子类
比如 object 变成 string,感受是不和谐的
协变和逆变是用来修饰泛型的 协变:out 逆变:in 用于在泛型中修饰泛型字母的,只有泛型接口和泛型委托能使用
// 作用
// 1.返回值 和 参数
// 用out修饰的泛型 只能作为返回值
internal delegate T TestOut();
// 用in修饰的泛型 只能作为参数
internal delegate void TestIn(T t);
// 2.结合里氏替换原则理解
internal class Father { }
internal class Son : Father { }
internal class Program
{
private static void Main(string[] args) {
Console.WriteLine("协变逆变");
// 作用(结合里氏替换原则理解)
// 协变 父类总是能被子类替换
// 看起来 就是 son ——> father
TestOut os = () => new Son();
TestOut of = os;
Father f = of(); // 实际上 返回的 是os里面装的函数 返回的是Son
// 逆变 父类总是能被子类替换
// 看起来像是 father——>son 明明是传父类 但是你传子类 不和谐的
TestIn iF = value => { };
TestIn iS = iF;
iS(new Son()); // 实际上 调用的是 iF
}
}
协变 out 逆变 in 用来修饰泛型替代符,只能修饰接口和委托中的泛型
作用两点:
- out 修饰的泛型类型只能作为返回值类型 ,in 修饰的泛型类型只能作为参数类型
- 遵循里氏替换原则,用 out 和 in 修饰的泛型委托,可以相互装载(有父子关系的泛型)
协变:父类泛型委托装子类泛型委托
逆变:子类泛型委托装父类泛型委托
十八、多线程private static void NewThreadLogic() {
// 新开线程 执行的代码逻辑 在该函数语句块中
while (isRuning)
// Thread.Sleep(1000);
// Console.WriteLine("新开线程代码逻辑");
{
lock (obj) {
Console.SetCursorPosition(10, 5);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("■");
}
}
}
// 线程类 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("●");
}
}
// 多线程对于我们的意义
//可以用多线程专门处理一些复杂耗时的逻辑
//比如 寻路、网络通信等等
多线程是多个可以同时执行代码逻辑的“管道” 可以通过代码开启多线程,用多线程处理一些复杂的可能影响主线程流畅度的逻辑 关键字 Thread
十九、预处理器指令 // 1
// #define
// 定义一个符号,类似一个没有值的变量
// #undef
// 取消define定义的符号,让其失效
// 两者都是写在脚本文件最前面
// 一般配合 if指令使用 或配合特性
// 2
// #if
// #elif
// #else
// #endif
// 和if语句规则一样,一般配合#define定义的符号使用
// 用于告诉编译器进行编译代码的流程控制
// 如果发现有Unity4这个符号 那么其中包含的代码 就会被编译器翻译
// 可以通过 逻辑或 和 逻辑与 进行多种符号的组合判断
#if Unity4
Console.WriteLine("版本为Unity4");
#elif Unity2017 && IOS
Console.WriteLine("版本为Unity2017");
// #warning 这个版本 不合法
// #error 这个版本不准执行
#else
Console.WriteLine("其它版本");
#endif
// 3
// #warning
// #error
// 告诉编译器
// 是报警告还是报错误
// 一般还是配合if使用
二十、反射
(一)概念
-
程序集
程序集是经由编译器编译得到的,供进一步编译执行的那个中间产物
在 WINDOWS 系统中,它一般表现为后缀为.dll(库文件)或者是.exe(可执行文件)的格式
程序集就是我们写的一个代码集合,我们现在写的所有代码
最终都会被编译器翻译为一个程序集供别人使用 比如一个代码库文件(dll)或者一个可执行文件(exe)
-
元数据
元数据就是用来描述数据的数据 这个概念不仅仅用于程序上,在别的领域也有元数据
程序中的类,类中的函数、变量等等信息就是程序的元数据 有关程序以及类型的数据被称为元数据,它们保存在程序集中
-
反射
程序正在运行时,可以查看其它程序集或者自身的元数据。 一个运行的程序查看本身或者其它程序的元数据的行为就叫做反射
在程序运行时,通过反射可以得到其它程序集或者自己程序集代码的各种信息 类,函数,变量,对象等等,实例化它们,执行它们,操作它们
-
反射的作用
因为反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性
- 程序运行时得到所有元数据,包括元数据的特性
- 程序运行时,实例化对象,操作对象
- 程序运行时创建新对象,用这些对象执行任务
(二)Type
Type(类的信息类) 它是反射功能的基础!它是访问元数据的主要方式。 使用 Type 的成员获取有关类型声明的信息 有关类型的成员(如构造函数、方法、字段、属性和类的事件)
internal class Test
{
private readonly int i = 1;
public int j;
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);
}
}
- 获取 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);
- 得到类的程序集信息
// 可以通过Type可以得到类型所在程序集信息
Console.WriteLine(type.Assembly);
Console.WriteLine(type2.Assembly);
Console.WriteLine(type3.Assembly);
- 获取类中的所有公共成员
// 首先得到Type
Type t = typeof(Test);
// 然后得到所有公共成员
// 需要引用命名空间 using System.Reflection;
MemberInfo[] infos = t.GetMembers();
for (int i = 0; i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?