文章目录
- 数据结构类
- `ArrayList`
- `Stack`
- `Queue`
- `Hashtable`
- 泛型
- 泛型类、泛型方法、泛型接口
- `List`
- `Dictionary`
- `LinkedList`
- 泛型栈,泛型队列
- 委托和事件
- 委托
- 事件
- 匿名函数
- `Lambad` 表达式
- **闭包**
- List 排序
- 逆变协变
- 多线程
- 进程
- 线程
- 多线程
- 方法:
- 线程之间共享数据:加锁
- 反射和特性
- 程序集
- 元数据
- 反射
- 获取Type
- 获取程序集信息
- `Activator`
- `Assembly`
数据结构类
ArrayList
-
方法:
// 申明:本质是object数组 ArrayList array = new ArrayList(); // 增 array.Add(10); array.Insert(1, 100); // (下标,插入的元素) // 删 array.Remove(10); // 删除指定元素,从下标0开始遍历找到第一个删除 array.RemoveAt(0); // 删除指定下标的元素 array.Clear(); // 清空 // 改 array[0] = 100; // 查 object obj1 = array[0]; // 获得指定下标元素 bool flag = array.Contains(123); // 查找元素是否存在 int index1 = array.IndexOf(99); // 正向查找元素下标,不存在放回-1 int index2 = array.LastIndexOf(100); // 方向查找元素下标,不存在返回-1 // 长度 / 容量 int count = array.Count; // 当前长度 int capacity = array.Capacity; // 当前容量 // 遍历 foreach(object item in array) // (遍历的类型 变量名称 int 遍历的对象) { Console.WriteLine(item); }
-
装箱拆箱
ArrayList
本质是一个可以自动扩容的object的数组,所以可能存在装箱拆箱 -
ArrayList
和数组的区别:ArrayList
本质是 object 数组,可以存储任何类型ArrayList
不是定长的有自己的容量,根据大小自动扩容,而数组是定长的
Stack
- 方法:
// Stack:栈 先进后出
// 申明
Stack stack = new Stack();
// 入栈
stack.Push(1.2f);
// 出站
stack.Pop();
// 查看
object obj = stack.Peek(); // 看栈顶
bool flag = stack.Contains(1.2f); // 查看元素是否在栈中
// 清空栈
stack.Clear();
// 长度
int count = stack.Count;
// 不支持随机访问,所以不能用下标遍历
// foreach 遍历
foreach(Object item in stack)
{
Console.WriteLine(item);
}
// 栈转数组
object[] objs = stack.ToArray();
// 也存在装箱拆箱
Queue
-
方法:
// Queue: 队列 先进先出 // 申明 Queue queue = new Queue(); // 进队列 queue.Enqueue(1.2f); // 出队列 queue.Dequeue(); // 查 Object obj = queue.Peek(); // 查对头 bool flag = queue.Contains(1.2f); // 查看元素是否在队列中 // 清队列 queue.Clear(); // 不支持随机访问,所以不能用下标遍历 // foreach遍历 foreach (Object item in queue) { Console.WriteLine(item); } // 转数组 Object[] objs = queue.ToArray(); // 长度 int count = queue.Count; // 存在装箱拆箱
Hashtable
-
方法:
// Hashtable:哈希表 键/值对 Hashtable hashtable = new Hashtable(); // 增 hashtable.Add(1, "123"); // 不能插入相同的键 // 删 hashtable.Remove(1); hashtable.Clear(); // 清空 // 查 Object obj = hashtable[1]; bool f1 = hashtable.Contains(1); // 通过key查找 bool f2 = hashtable.ContainsValue(1); // 通过value查找 // 改 hashtable[1] = 1.10f; // 遍历 // 遍历所有键 foreach (object item in hashtable.Keys) { Console.WriteLine(item); Console.WriteLine(hashtable[item]); // 获得值 } // 遍历所有值 foreach (object item in hashtable.Values) { Console.WriteLine(item); } // 遍历键与值 foreach (DictionaryEntry item in hashtable) { Console.WriteLine(item.Key + " " +item.Value); }
泛型
泛型类、泛型方法、泛型接口
-
泛型实现了类型参数化,达到代码重用的目的,泛型相对于类型占位符,定义类或者方法时使用替代符代表变量类型,当真正使用类或者方法时再具体指定类型
-
方法:
// 泛型类 class Calculate<T> { public T a, b; public T1 add<T1>(T1 x, T1 y) // 泛型方法 { return x; } } // 泛型接口 class ICalculate<T> { public T Value { get; set; } } // 继承的时候需要指名泛型
List
-
方法:
// List:本质是泛型数组ArrayList // 申明 List<int> list = new List<int>(); // 增加 list.Add(22); list.Insert(0, 99); // 指定下标插入 // 删 list.Remove(22); list.Clear(); // 清空 // 查 int i = list[0]; bool f = list.Contains(22); // 正向查找、反向查找 int index1 = list.IndexOf(22); int index2 = list.LastIndexOf(22);
Dictionary
-
方法:
// Dictionary:本质是泛型的Hashtable // 申明 Dictionary<int, string> dic = new Dictionary<int, string>(); // 增 dic.Add(1, "pap"); // 删 dic.Remove(1); dic.Clear(); // 清空 // 查 string s = dic[1]; // Hashtable找不到键返回空,Dictionary不运行查找不存在的键值 bool f1 = dic.ContainsKey(1); // 根据键查找 bool f2 = dic.ContainsValue("pap"); // 根据值查找 // 改 dic[1] = "mmm"; // 遍历 foreach (int item in dic.Keys) { Console.WriteLine(dic[item]); } foreach (string item in dic.Values) { Console.WriteLine(item); } foreach (KeyValuePair<int, string> item in dic) { Console.WriteLine(item.Key + " " + item.Value); }
LinkedList
-
方法:
// LinkedList:本质泛型双向链表 // 申明 LinkedList<string> linked = new LinkedList<string>(); // 增 linked.AddLast("a"); // 尾插 linked.AddFirst("b"); // 头插 LinkedListNode<string> node = linked.Find("b"); linked.AddAfter(node, "66"); // 在指定结点的后插入 linked.AddBefore(node, "66"); // 在指定结点的前面插入 // 删 linked.RemoveLast(); // 尾删 linked.RemoveFirst(); // 头删 linked.Remove("b"); // 删除指定元素 linked.Clear(); // 清空 // 查 LinkedListNode<string> node1 = linked.First; // 头结点 LinkedListNode<string> node2 = linked.Last; // 头结点 LinkedListNode<string> node3 = linked.Find("c"); // 查找指定元素 Console.WriteLine(node3.Value); // value值 Console.WriteLine(node3.Next); // 下一个结点 bool f = linked.Contains("a"); // 改 linked.First.Value = "500"; // 遍历 LinkedListNode<string> p = linked.First; while (p != null) { Console.WriteLine(p.Value); p = p.Next; } LinkedListNode<string> p2 = linked.Last; while (p2 != null) { Console.WriteLine(p.Value); p = p.Previous; }
泛型栈,泛型队列
Stack
与Queue
是支持泛型使用的,方法参考object的栈和队列
委托和事件
委托
-
委托是一种数据类型,是存有对某个方法的引用的一种引用类型变量。委托是函数的容器,用来存储、传递函数
-
格式:
public delegate void Print(string s); // 申明委托 class Person { public Print print1; // 定义委托 public Print print2; public Person(Print p1, Print p2) { print1 = p1; // 委托赋值 print2 = p2; } public void toTest(string s1, string s2) { print1.Invoke("*"); // 调用委托 Console.WriteLine("----------我是分割线----------"); print2.Invoke("#"); // 调用委托 } }
-
多播委托
委托变量可以存储多个函数
// 多播委托:委托变量可以存储多个函数 public delegate void Fun(int x); class Program { static void Main(string[] args) { Fun fun = f1; fun += f2; // 增加委托 fun.Invoke(56); // 执行f1 、f2 fun -= f2; // 移除委托 } static void f1(int x) { Console.WriteLine(x % 100); } static void f2(int x) { Console.WriteLine(x % 10); } }
-
系统提供委托
static void Main(string[] args) { // 系统自带委托 // 无参无返回的委托 Action action = f1; // 泛型委托 Func<string> funcString = f2; // 返回值为泛型的无参委托 Action<int,int> act = f3; // 传入n个参数的无返回值的委托 Func<int,int> funcI = f4; // 前n个泛型是参数,最后一个泛型是返回值 } static void f1 () { Console.WriteLine("我是无参无返回值的函数"); } static string f2 () { return "我是有返回无的函数"; } static void f3 (int x, int y) { Console.WriteLine("我是有参无返回的函数"); int sum = x + y; } static int f4 (int x) { Console.WriteLine("我是有参有返回的函数"); return x; }
事件
-
事件是基于委托存在,事件是委托的安全包裹。为了防止外部随意置空和调用事件,事件相对于对委托进行了一次封装,更加安全
-
格式:
class Test { // 访问修饰符 event关键字 委托类型 事件名 public event Action testEve; public Test() { testEve = fun; // 赋值 testEve += fun; testEve -= fun; testEve(); // 调用 testEve.Invoke(); } public void fun() { Console.WriteLine("我是无参无返回的函数"); } }
-
注意:
事件不能在类外部赋值和调用,只能在类内部封装和调用
事件不可以作为临时变量
class Program { static void Main(string[] args) { // 事件不能在类外部赋值和调用,只能在类内部封装和调用 Test t = new Test(); // (X) t.testEve = null; 事件不能在类外部赋值 // (X) t.testEve(); 事件不能在类外部调用 t.testEve += Ftest; // 事件可以在类外部增删 // 事件不可以作为临时变量 } public static void Ftest() { Console.WriteLine("我是测试函数"); } }
匿名函数
-
没有函数名的的函数,需要配合委托和事件使用
-
格式
// delegate (参数列表) // { // 函数体 // }; // 配合委托使用 Action act = delegate () { Console.WriteLine("无参无返回匿名函数"); }; Func<string> func = delegate() { return "有返回值的匿名函数"; };
-
使用:
public static void Test(int a, Action act) // 作为参数传入 { Console.WriteLine(a); act.Invoke(); } public static Action Test2() // 作为返回值 { return delegate (){ Console.WriteLine("匿名函数做返回值"); }; } static void Main(string[] args) { Test(10, delegate (){ Console.WriteLine("匿名函数做参数"); }); // 看起来怪怪的写法 Test2()(); // 调用Test2()函数,返回一个委托,调用委托Action() }
-
匿名函数缺点:没有办法从委托或事件中指定移除函数
Lambad
表达式
-
是匿名函数的简写,配合委托或事件使用
-
格式
// (参数列表) =>
// {
// // 函数体
// };
// 有参
Action<int> a = (int value) =>
{
Console.WriteLine(value);
};
// Lambad表达式省略参数类型,类型与委托或事件容器一致
Action<string> a2 = (str) =>
{
Console.WriteLine(str);
};
闭包
-
内层函数可以引用包含在它外层的函数变量,即使外层函数的执行已经终止
class Test { public event Action a; public Test() { // 正常情况下value的生命周期为这个构造函数执行完,因为是一个临时变量 int value = 10; // 形成闭包 a = () => { // 事件使用了这个变量,改变了临时变量的生命周期,当事件置空时释放 Console.WriteLine("value临时变量:" + value); }; } public void DoAction() { a.Invoke(); // 事件不能在类外使用,所以封装一层来使用 } } static void Main(string[] args) { Test t = new Test(); // 执行构造函数,临时变量的生命周期被改变形成了闭包 t.DoAction(); }
打印: value临时变量:10
-
该变量的值并非创建时的值,而是在父函数范围内的最终值
class Test { public event Action a; public Test() { int value = 10; for (int i = 0; i < 10; i++) // 为委托添加10个函数 { a += () => { Console.WriteLine(i); // 最后打印的是i的最终值10 }; } } public void DoAction() { a.Invoke(); } } static void Main(string[] args) { Test t = new Test(); t.DoAction(); }
打印:10 10 10 10 10 10 10 10 10 10
-
利用临时变量
class Test { public event Action a; public Test() { int value = 10; for (int i = 0; i < 10; i++) { // 这个临时变量每循环一次就释放并重新创建一次,所以指向的是不同的值 int index = i; a += () => { Console.WriteLine(index); }; } } public void DoAction() { a.Invoke(); } } static void Main(string[] args) { Test t = new Test(); t.DoAction(); }
打印:0 1 2 3 4 5 6 7 8 9
List 排序
-
sort 排序
List<int> list = new List<int>(); list.Add(10); list.Add(1); list.Add(5); list.Sort();
-
sort 自定义排序
class Item : IComparable<Item> // 继承IComparable泛型接口
{
int value;
public Item(int value)
{
this.value = value;
}
public int CompareTo(Item other) // 实现接口的比较函数
{
// 返回值的含义:
// 小于0,放在other前
// 等于0,不变
// 大于0,放在other后
// 升序排列
if (this.value < other.value)
return -1;
return 1;
}
}
- sort 委托自定义排序
class Item
{
public int value;
public Item(int value)
{
this.value = value;
}
}
class Program
{
// 也可以写成匿名函数
static int SortItem(Item item1, Item item2) // 返回类型参数列表参照委托
{
return item1.value < item2.value ? -1 : 1; // 升序排列
}
static void Main(string[] args)
{
List<Item> list = new List<Item>();
list.Add(new Item(10));
list.Add(new Item(1));
list.Add(new Item(6));
list.Sort(SortItem); // 为委托添加函数
}
}
逆变协变
-
协变:父类可以装载子类,是和谐的自然的变化
逆变:子类不能装载父类,是逆变换不自然的变化
-
协变关键字
out
,逆变关键字in
,用于泛型中修饰泛型字母 -
只有泛型接口和泛型委托能够使用
-
定义:
// 协变 out // 用out修饰的泛型只能作为返回值类型,不能作为参数类型 delegate T Fuc1<out T>(); // 可以作为返回值类型 // delegate T Fuc1<out T>(T value); 不能作为参数类型 // 逆变 in // 用in修饰的泛型只能作为参数类型,不能作为返回值类型 delegate void Fuc2<in T>(T value); // 可以作为参数类型 // delegate T Fuc2<in T>(T value); // 不能作为返回值类型
-
使用:
// 协变 // 泛型为Son的委托 Fuc1<Son> f = () => { return new Son(); }; // 委托的赋值需要返回值参数类型都一致才能成功 // 而这里的返回值是不一样的,但是可以赋值成功 // 因为Fuc1委托的泛型返回值类型定义了 协变out ,此时会自动判断返回值的父子类关系,父类能够转载子类 Fuc1<Father> f2 = f; // 返回的是Father对象,实际上装载的是Son对象 Father father = f2(); // 逆变 // 泛型为Father的委托,Lambad表达式省略参数类型 Fuc2<Father> fn = (father) => {}; // 因为Fuc2委托的参数类型定义了 逆变 in,此时子类可以赋值父类 Fuc2<Son> fn2 = fn; // 调用fn2传入子类类型,Fn2实际上是Father泛型,参数中父类可以装载子类,满足里氏替换原则 fn2(new Son());
多线程
进程
-
一个应用程序就相对于开启了一个进程
-
进程之间可以相互独立运行、互不干扰
-
进程之间也可以相互访问、操作
线程
-
操作系统进行运算的最小调度单位,包含在进程之中,是进程的实际运作单位
-
一个进程中可以并发多个进程
-
编写在主函数中的程序为主线程
多线程
- 可以同时运行代码的多条“管道”就叫多线程
方法:
static void Main(string[] args)
{
// 1.申明线程
Thread thread = new Thread(NewThread); // 参数是一个无参无返回的委托
// 2.开启线程
thread.Start();
// 3.设置为后台线程
// 默认是前台线程,主线程结束了新开线程不会结束
// 设置为后台线程后,主线程结束新开线程也会结束
thread.IsBackground = true;
// 4.关闭释放线程
// 第一种方法
thread = null;
// 第二种方法:在.Net core版本中无法中止会报错
thread.Abort();
// 线程休眠
// 单位是ms,在哪个线程中调用就是休眠哪一个线程,这里是休眠主线程
Thread.Sleep (1000);
}
static void NewThread()
{
Console.WriteLine("我是一个新开的线程");
}
线程之间共享数据:加锁
多个线程之间使用的内存都是共享的,都属于该进程,所以当多线程同时操作同一片内存区域时可能会出现问题
下面这段代码如果不加锁的话会出现逻辑执行错误,两个线程间的逻辑语句出现混乱
class Program
{
static object lockObj;
static void Main(string[] args)
{
// lock:当在多个线程中想要访问同一个东西时,避免逻辑顺序执行的差错
// lock(引用类型)
// 为引用类型加锁,此时会先锁住(等待)其他地方调用完在开锁进入
while (true)
{
lock(lockObj)
{
Console.SetCursorPosition(0, 0);
Console.ForegroundColor = ConsoleColor.Yellow;
Console.Write("0");
}
}
}
static void NewThread()
{
while(true)
{
lock(lockObj)
{
Console.SetCursorPosition(10, 10);
Console.ForegroundColor = ConsoleColor.Blue;
Console.Write("1");
}
}
}
}
反射和特性
程序集
- 程序集是由编译器编译得到,可供进一步编译执行的代码集合
- 在Windows操作系统中,一般表现为
.dll
的库文件和.exe
的可执行文件
元数据
- 程序中的类,函数,变量等等信息就是程序的元数据
- 有关程序以及类型的数据被称为元数据,被保持在程序集中
反射
- 程序运行时,访问和查看其他程序集或自身的元数据就叫反射
- 反射可以在程序编译后获得信息,所以它提高了程序的拓展性和灵活性
获取Type
// Type类 (信息类)
// 用来获取有关类型声明的信息
// 获取Type
// object中的GetType方法获取
int a = 32;
Type t1 = a.GetType(); // 这里得到的是int这个类的信息
// typeof方法获取,参数传入对应类
Type t2 = typeof(int);
// 通过Type静态方法获取,参数类名字符串必须包括命名空间
Type t3 = Type.GetType("System.Int32"); // 加上命名空间才能成功获取
t1、t2、t3指向的都是同一个内存空间
获取程序集信息
// 一、【获取类的所有公共成员】
Type t = typeof(int);
MemberInfo[] memInfos = t.GetMembers();
// 二、【获取类的公共构造函数并使用】
// 1.获取所有构造函数
ConstructorInfo[] ctors = t.GetConstructors();
// 2.获取一个构造函数
// 参数Type数组,数组内容为按顺序的参数类型
// 获得无参构造
ConstructorInfo info1 = t.GetConstructor(new Type[0]);
// 获得有参构造
ConstructorInfo info2 = t.GetConstructor(new Type[1] {typeof(int)});
// 执行
// 返回类型是 object
// 调用无参构造
int i = (int) info1.Invoke(null); // 无参构造需要传入null
// 调用有参构造:参数是object数组
object obj = info2.Invoke(new object[] {2});
// 三、【获取类的公共成员变量】
// 1.得到所有成员变量
FieldInfo[] fieldInfos = t.GetFields();
// 2.得到指定名称的公共成员变量
// 没有获取值,只是得到了这个成员变量名
FieldInfo fieldInfo = t.GetField("value"); // 变量传变量名
// 得到成员变量的值
int test = 10;
fieldInfo.GetValue(test); // 得到这个对象对应的成员变量
// 设置成员变量的值
fieldInfo.SetValue(test, 100);
;
// 四、【获取类的公共成员方法】
// 1.得到所有成员方法
MethodInfo[] methods = t.GetMethods();
// 2.得到指定成员方法
// 参数:(函数名称,Type数组 参数类型)
MethodInfo method = t.GetMethod("CompareTo", new Type[] {typeof(int)});
// 调用函数
int v = 100;
// 参数:(对象,object数组)
method.Invoke(v, new object[] {10});
Activator
-
能够快速实例化对象的类
// Activator // 用于快速实例化 Type type = typeof(Test); // Test是一个类 // 1.调用无参构造 // 返回的是object Test test1 = Activator.CreateInstance(type) as Test; // 2.有参构造 // 参数:(类,参数值[变长]) Activator.CreateInstance(type, 99); //调用int参数的构造函数
Assembly
-
程序集类
主要用来加载其他程序集中的元数据,当要使用其他程序集的内容时需要先加载
// Assembly // 三种加载程序集的函数 // 1.一般用来加载在同一文件夹下的其他程序集 Assembly assembly1 = Assembly.Load("程序集名称"); // 2.一般用来加载不在同一文件夹下的其他程序集 // 方法一:Assembly.LoadFrom("包含程序集清单的文件名称或路径"); Assembly assembly2 = Assembly.LoadFrom("C:\\Users……"); // 注意是双斜杠 Assembly assembly3 = Assembly.LoadFrom(@"C:\Users……"); // 或者用@取消转义字符 // 方法二:Assembly assembly3 = Assembly.LoadFile("要加载的文件的完全限定路径"); // 通过Assembly获得所有Type Type[] types = assembly1.GetTypes(); // 通过Assembly得到指定类Type Type type1 = assembly1.GetType("Program.Test");