一、ArrayList
using System.Collections;
namespace Lesson1_ArrayList
{
#region 背包类练习
class Backpack
{
float money;
ArrayList backpack;
public Backpack(float money)
{
this.money = money;
backpack = new ArrayList();
}
public string StoreItems(Item item)
{
backpack.Add(item);
return $"物品" + item.name + "已存入背包";
}
public string BuyItem(Item item)
{
backpack.Add(item);
this.money -= item.price;
return $"物品" + item.name + "已购买并存入背包";
}
public string SellItem(string name)
{
foreach(Item item in backpack)
{
if(item.name == name)
{
backpack.Remove(item);
this.money += item.price;
return $"物品" + item.name + "已卖出";
}
}
return "未找到该物品";
}
public string ShowItemFun(string name)
{
foreach (Item item in backpack)
{
if (item.name == name)
{
return $"物品" + item.name + "功能是:" + item.function;
}
}
return "未找到该物品";
}
public float ShowMoney()
{
return this.money;
}
}
class Item
{
public string name;
public float price;
public string function;
public Item(string name, float price, string function)
{
this.name = name;
this.price = price;
this.function = function;
}
}
#endregion
internal class Program
{
static void Main(string[] args)
{
#region 定义
ArrayList array = new ArrayList();
ArrayList array1 = new ArrayList();
#endregion
#region 增
array.Add(123);//增加一个元素
Console.WriteLine(array[0]);//>>123
array1.Add(1);
array.AddRange(array1);//增加一组
array.Insert(1, 4565);//插入指定元素到指定位置
#endregion
#region 删
array.Remove(123);//寻找相关元素并删除
Console.WriteLine(array[0]);//>>1
array.RemoveAt(0);//删除指定索引处
array.Clear();//清空元素
#endregion
#region 查
array.Add(1);
array.Add(2);
Console.WriteLine(array[0]);//查看指定索引处元素>>1
bool b = array.Contains(2);//查看是否具有该元素,返回bool
Console.WriteLine(b);//>>True
Console.WriteLine(array.IndexOf(1));//查找指定元素的索引位置,找不到返回-1>>0
Console.WriteLine(array.LastIndexOf(2));//查找指定元素,并返回从后往前遇到的第一个元素,但返回值为从前往后索引数。
#endregion
#region 改
array[0] = 00;
Console.WriteLine(array[0]);//>>0
#endregion
#region 遍历
Console.Write(array.Count);//得到集合的Count,当前元素数量
Console.WriteLine(array.Capacity);//得到容量
for(int i = 0; i < array.Count; i++)
{
Console.WriteLine(array[i]);
}
//迭代器
foreach (object item in array)
{
Console.WriteLine(item);
}
#endregion
#region 装箱拆箱
//ArrayList因为是一个Object类的数组,所以存在由栈上的值类型转换为堆上的引用类型的转换,所以存在装箱拆箱。
#endregion
#region 练习
//请简述ArrayList和数组的区别
//ArrayList可以存储不同的类型,可以不指定长度,某些方法不同,数组长度是Length,ArrayList是Count。
//背包测试
Backpack backpack = new Backpack(0);
Item phone = new Item("手机", 3500, "打电话");
backpack.StoreItems(phone);
Console.WriteLine(backpack.ShowItemFun("手机"));
Item gun = new Item("枪", 5000, "开火");
backpack.BuyItem(gun);
Console.WriteLine(backpack.ShowItemFun("枪"));
backpack.SellItem("枪");
Console.WriteLine(backpack.ShowItemFun("枪"));
backpack.SellItem("手机");
float money = backpack.ShowMoney();
Console.WriteLine(money);
#endregion
}
}
}
二、Stack
using System.Collections;
using System.Security.Cryptography.X509Certificates;//需要引入命名空间
namespace Leeson2_Stack
{
internal class Program
{
static void Main(string[] args)
{
#region 声明
//先进后出
Stack stack = new Stack();
#endregion
#region 增
//压栈
stack.Push(1);
#endregion
#region 取
//栈中不存在删除,只有取,也叫弹栈。
object o = stack.Pop();//一次取一个
Console.WriteLine(o);
#endregion
#region 查看
//只能查看栈顶位置的元素
stack.Push(2);
o = stack.Peek();
Console.WriteLine(o);
//检查是否存在元素
Console.WriteLine(stack.Contains(2));
#endregion
#region 改
//更改底部元素需要清空。
stack.Clear();
stack.Push(1);
stack.Push(2);
stack.Push(3);
#endregion
#region 遍历
//长度
Console.WriteLine("开始遍历");
Console.WriteLine(stack.Count);
//第一种遍历方法:
foreach (object item in stack)
{
Console.WriteLine(item);
}
//第二种遍历方法:
Console.WriteLine("第二种遍历开始");
Object[] objects = stack.ToArray();
for (int i = 0; i < objects.Length; i++)
{
Console.WriteLine(objects[i]);
}
Console.WriteLine("第三种遍历");
//第三种遍历方法:
while (stack.Count > 0)
{
object o1 = stack.Pop();
Console.WriteLine(o1);
}
#endregion
#region 练习
//1、栈的存储规则是先进后出
fun1();
#endregion
}
//2、转换二进制
public static void fun1()
{
Stack stack = new Stack();
if(int.TryParse(Console.ReadLine(), out int num))
{
while(num != 0)
{
stack.Push(num % 2);
num /= 2;
//Console.WriteLine(num);
}
}
else
{
Console.WriteLine("输入错误");
}
foreach (object item in stack)
{
Console.WriteLine(item);
}
}
}
}
三、Queue
using System.Collections;
namespace Lesson3_Queue
{
internal class Program
{
static void Main(string[] args)
{
#region 基础
//1、本质是一个Object数组,2、封装了特殊的规则,3、先进先出。
//使用需要声明命名空间System.Collections。
//因为是object数组所以也存在装箱拆箱操作。
#endregion
#region 增
Queue queue = new Queue();
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
#endregion
#region 取
object num = queue.Dequeue();
Console.WriteLine("取:" + num);
#endregion
#region 查
num = queue.Peek();//查看队列第一个元素
Console.WriteLine("查:" + num);
Console.WriteLine("包含:" + queue.Contains(1));//看是否包含该元素
#endregion
#region 改
//只能先清空再增加
queue.Clear();
queue.Enqueue(1);
queue.Enqueue(2);
queue.Enqueue(3);
#endregion
#region 遍历
//长度
Console.WriteLine("长度:" + queue.Count);
//foreach循环
Console.WriteLine("foreach循环:");
foreach (object item in queue)
{
Console.WriteLine(item);
}
//转换object数组
Object[] objects = queue.ToArray();
for(int i = 0; i < objects.Length; i++)
{
Console.WriteLine($"第{i}个元素为{objects[i]}");
}
//循环出栈
while (queue.Count > 0)
{
Console.WriteLine($"取出{queue.Dequeue()}");
}
#endregion
#region 队列的存储规则
//先进先出,只能存一个,取一个。
//定时打印消息
Queue queue1 = new Queue();
queue1.Enqueue("1111");
queue1.Enqueue("2222");
queue1.Enqueue("3333");
queue1.Enqueue("4444");
queue1.Enqueue("5555");
queue1.Enqueue("6666");
queue1.Enqueue("7777");
queue1.Enqueue("8777");
queue1.Enqueue("9777");
queue1.Enqueue("10777");
object[] objects1 = queue1.ToArray();
int now = DateTime.Now.Second;
int future;
int count = 0;
while (count < 10)
{
future = DateTime.Now.Second;
if(future - now > 0.5)
{
now = DateTime.Now.Second;
Console.WriteLine(objects1[count]);
count++;
}
}
#endregion
}
}
}
四、Hashtable
using System.Collections;
namespace Lesson4_HashTable
{
#region 练习二:怪物管理器
class Monster
{
public string name;
public Monster(string name)
{
this.name = name;
}
}
class MonsterManager
{
Hashtable monsterTable;
public MonsterManager()
{
monsterTable = new Hashtable();
}
public void CreateMonster(int id, Monster monster)
{
monsterTable.Add(id, monster);
}
public void RemoveMonster(int id)
{
monsterTable.Remove(id);
}
public void PrintMonter()
{
foreach(object item in monsterTable.Keys)
{
Console.WriteLine($"键为:{item},值为:{(monsterTable[item] as Monster).name}");
}
}
}
#endregion
internal class Program
{
static void Main(string[] args)
{
#region 基础
//HashTable(又称散列表)是基于键的哈希代码组织起来的键值对。
//主要作用是提高查询数据的效率
//使用键来访问集合中的元素
#endregion
#region 声明
//使用需要引用命名空间System.Collections
Hashtable hashtable = new Hashtable();
#endregion
#region 增
//不能出现相同的键
hashtable.Add(1, 12);
hashtable.Add(true, "123");
#endregion
#region 删除
//只能删除不存在的键
hashtable.Remove(true);
//清空
hashtable.Clear();
#endregion
#region 查
//1、通过键来查看值,中括号中填入的是键值,而不是数组下标,找不到会返回空
hashtable.Add(1, "1");
hashtable.Add(2, "Hello");
hashtable.Add("123", 123);
hashtable.Add(123, "123");
Console.WriteLine(hashtable[1]);
//2、查看是否存在
if (hashtable.Contains("123"))//或者使用ContainsKey也可以达到相同效果
{
Console.WriteLine(hashtable["123"]);
}
if (hashtable.ContainsValue(123))
{
Console.WriteLine("有");
}
#endregion
#region 改
//通过键去改
hashtable[1] = "一";
#endregion
#region 遍历
//得到键值对数量
Console.WriteLine(hashtable.Count);
//遍历键
Console.WriteLine("遍历键");
foreach(Object item in hashtable.Keys)
{
Console.Write("键:" + item);
Console.WriteLine(" 值:" + hashtable[item]);
}
//遍历值
Console.WriteLine("遍历值");
foreach (Object item in hashtable.Values)
{
Console.WriteLine(item);
}
//键值一起遍历
Console.WriteLine("遍历键和值");
foreach(DictionaryEntry item in hashtable)
{
Console.WriteLine(item.Key);
Console.WriteLine(item.Value);
}
//迭代器循环
Console.WriteLine("迭代器循环开始:");
IDictionaryEnumerator dictionary = hashtable.GetEnumerator();
bool flag = dictionary.MoveNext();
while (flag)
{
Console.WriteLine("键为:" + dictionary.Key + ", 值为:" + dictionary.Value);
flag = dictionary.MoveNext();
}
#endregion
#region 练习一
//请描述Hashtable的存储规则
//Hashtable的存储规则是:存储键值对,键不可以重复。
#endregion
//测试练习二
Monster m1 = new Monster("大大怪");
MonsterManager monsterManager = new MonsterManager();
monsterManager.CreateMonster(1, m1);
monsterManager.PrintMonter();
}
}
}
五、泛型
- 泛型实现了类型参数化,一份代码操作多种类型,达到代码复用。
- 泛型相当于类型占位符,定义类或方法时使用替代符代表变量类型,使用时再指定具体类型。
- 基本语法:
- class 类名<泛型占位字母>
- interface 接口名<泛型占位字母>
- 泛型函数语法:
- 函数名<泛型占位字母>(字母 名称)
- 泛型占位字母可以有多个,用,号隔开。
using System.Runtime.InteropServices;
using System.Text;
namespace Lesson5_泛型
{
class Common()
{
public string DetermineType<T>(T unknown)
{
Type type = unknown.GetType();
switch (type.FullName)
{
case "System.Int32":
return $"整形,{Marshal.SizeOf(type)}字节";
case "System.Char":
return $"字符,{Marshal.SizeOf(type)}字节";
case "System.Single":
return $"字符,{Marshal.SizeOf(type)}字节";
case "System.String":
string str = unknown as string;
return $"字符,{Encoding.UTF8.GetByteCount(str)}字节";
default:
return "其他类型";
}
}
}
internal class Program
{
static void Main(string[] args)
{
Common c1 = new Common();
string str1 = c1.DetermineType<double>(4522);
Console.WriteLine(str1);
}
}
}
六、泛型约束
- 是什么:用于限制泛型类型,共有6种类型:
| 类型 | 格式 |
|---|---|
| 值类型 | where 泛型字母:struct |
| 引用类型 | where 泛型字母:class |
| 存在无参构造函数 | where 泛型字母:new() |
| 某个类本身或者其派生类 | where 泛型字母:类名 |
| 某个接口的派生类型 | where 泛型字母:接口名 |
| 另一个泛型类型本身或者派生类型 | where 泛型字母:另一个泛型字母 |
- 约束直接可以联合使用,中间用","号隔开,一般new()是写在最后的。
- 对多个泛型约束,在后面再加where相关语句就行,并且两个where约束之间用空格隔开
namespace Lesson6_泛型约束
{
#region 练习:实现单例模式基类
class SingleBase<T> where T : new()
{
private static T instance = new T();
public SingleBase()
{
}
public static T Instance
{
get
{
return instance;
}
}
}
class Person : SingleBase<Person>
{
public int value;
}
#endregion
internal class Program
{
static void Main(string[] args)
{
Person.Instance.value = 10;
}
}
}
七、List
using System.Collections.Generic;
namespace Lesson7_List
{
internal class Program
{
static void Main(string[] args)
{
#region 声明和基础
//1.需要引用:using System.Collections.Generic;
List<int> list = new List<int>();
#endregion
#region 增
list.Add(1);
//范围增加
list.AddRange(list);
//插入
list.Insert(0, 2);
#endregion
#region 删
//删除指定元素
list.Remove(1);
//删除指定位置元素
list.RemoveAt(0);
//清空
list.Clear();
#endregion
#region 查
list.Add(1);
//查指定位置
Console.WriteLine(list[0]);
//是否存在某元素
Console.WriteLine(list.Contains(1));
//正向查找并返回位置,否则为-1
Console.WriteLine(list.IndexOf(1));
//反向查找
Console.WriteLine(list.LastIndexOf(1));
#endregion
#region 改
list[0] = 2;
#endregion
#region 遍历
Console.WriteLine("长度:");
Console.WriteLine(list.Count);
Console.WriteLine("容量" + list.Capacity);
//for遍历
for (int i = 0; i < list.Count; i++)
{
Console.WriteLine($"第{i}个元素是:{list[i]}");
}
//foreach遍历
foreach (int item in list)
{
Console.WriteLine(item);
}
#endregion
#region 练习1:描述List和ArrayList的区别
/* 1、list是泛型数组,不是object数组,所以需要指定类型,
* 2、list不需要进行装箱拆箱。
*/
#endregion
#region 练习遍历、增、删
List<int> ints = new List<int>();
for (int i = 10; i > 0; i--)
{
ints.Add(i);
}
ints.RemoveAt(4);
foreach (int item in ints)
{
Console.WriteLine(item);
}
#endregion
#region 练习测试
Boss boss = new Boss();
Gablin g = new Gablin();
foreach (Monster item in boss.Lists)
{
item.Attack();
}
#endregion
}
}
#region 遍历怪物类产生攻击
class Monster
{
private static List<Monster> lists = new List<Monster>();
public Monster()
{
lists.Add(this);
}
public List<Monster> Lists
{
get
{
return lists;
}
}
public void Attack()
{
Console.WriteLine(this + "的攻击");
}
}
class Boss : Monster
{
}
class Gablin : Monster
{
}
#endregion
}
八、Dictionary
namespace Lesson8_Dectionary
{
internal class Program
{
static void Main(string[] args)
{
#region 声明和基础
//需要引入命名空间 System.Collection.Generic
//Dectionary是需要制定类型的键值对。
Dictionary<int, string> d = new Dictionary<int, string>();
#endregion
#region 增
//单个增加,不能有重复键
d.Add(1, "123");
#endregion
#region 删
//根据键来删除
d.Remove(1);
//清空
d.Clear();
#endregion
#region 查
//根据键查询,没找到会报错
d.Add(1, "111");
Console.WriteLine(d[1]);
//根据键或值判断是否存在
Console.WriteLine(d.ContainsKey(1));
Console.WriteLine(d.ContainsValue("1"));
#endregion
#region 改
//根据查的方法来改
d[1] = "1111111";
#endregion
#region 遍历
d.Add(2, "22222222");
d.Add(5, "555555555");
d.Add(4, "4444444444444444");
//遍历键,顺便遍历值
foreach (int item in d.Keys)
{
Console.WriteLine("键是:" + item + ",值是:" + d[item]);
}
//遍历值
//将d.Keys改为Values即可
//一起遍历
foreach (KeyValuePair<int, string> item in d)
{
Console.WriteLine("键是:" + item.Key + ",值是:" + item.Value);
}
#endregion
//练习1测试
ConvertInput();
//练习2
Dictionary<char, int> dic = new Dictionary<char, int>();
string str = "Hello";
foreach (char i in str)
{
if (dic.ContainsKey(i))
{
dic[i]++;
}
else
{
dic.Add(i, 1);
}
}
foreach(KeyValuePair<char, int> pair in dic)
{
Console.WriteLine("键:" + pair.Key + ", 值:" + pair.Value);
}
}
#region 练习1:使用字典存储大写字
static void ConvertInput()
{
string str = "";
Console.WriteLine("请输入1~3位数"); // 先输出提示
string input = Console.ReadLine(); // 获取用户输入
// 可选:添加输入验证
if (input.Length > 0 && input.Length <= 3)
{
Dictionary<int, string> d = new Dictionary<int, string>
{
{1, "壹"},
{2, "贰"},
{3, "叁"},
{4, "肆"},
{5, "伍"},
{6, "陆"},
{7, "柒"},
{8, "捌"},
{9, "玖"}
};
char[] chars = input.ToCharArray();
for (int i = 0; i < chars.Length; i++)
{
switch (chars[i])
{
case '1':
str += '壹';
break;
case '2':
str += '贰';
break;
case '3':
str += '叁';
break;
case '4':
str += '肆';
break;
case '5':
str += '伍';
break;
case '6':
str += '陆';
break;
case '7':
str += '陆';
break;
case '8':
str += '捌';
break;
case '9':
str += '玖';
break;
}
}
}
else
{
Console.WriteLine("输入不符合要求");
}
Console.WriteLine(str);
}
#endregion
}
}
九、顺序存储和链式存储
namespace Lesson9_顺序存储和链式存储
{
internal class Program
{
static void Main(string[] args)
{
#region 常用的数据结构
//有1、栈 2、队列 3、数组 4、图 5、链表 6、树 7、堆 8、散列表
#endregion
#region 顺与链的区别
//顺序存储内存地址是按顺序排列的,连续的;链式则是随机的
#endregion
//双向链表测试
LikeList<int> likeList = new LikeList<int>();
likeList.Add(1);
Console.WriteLine("个数:" + likeList.Count());
likeList.Remove(1);
Console.WriteLine("个数:" + likeList.Count());
}
}
#region 双向链表
class Node<T>
{
public T data;
public Node<T> prev;
public Node<T>? next;
public Node(T data)
{
this.data = data;
}
}
class LikeList<T>
{
Node<T>? head;
Node<T>? last;
public int Count()
{
if (head == null) return 0;
Node<T> current = head;
int count = 0;
while(current != null)
{
Console.WriteLine(current.data);
count++;
current = current.next;
}
return count;
}
public void Add(T data)
{
if(head == null)
{
head = new Node<T>(data);
last = head;
}
else if(last == head && head != null)
{
last = new Node<T>(data);
head.next = last;
last.prev = head;
}
else
{
last.next = new Node<T>(data);
last.next.prev = last;
last = last.next;
}
}
public void Remove(T data)
{
if (head == null) return;
if (head.data.Equals(data))
{
if (head == last)
{
head = null;
last = null;
}
else
{
head.next.prev = null;
head = head.next;
}
}
else if (last.data.Equals(data))
{
if (head == last)
{
head = null;
last = head;
}
else
{
last = last.prev;
last.next = null;
}
}
else
{
Node<T> current = head.next;
while (current != null)
{
if (current.data.Equals(data))
{
current.prev.next = current.next;
current.next.prev = current.prev;
return;
}
current = current.next;
}
}
}
}
#endregion
}
十、LinkedList
using System.Collections.Generic;
namespace Lesson10_LinkedList
{
internal class Program
{
static void Main(string[] args)
{
#region 基础
//本质是一个泛型双向链表
LinkedList<int> ints = new LinkedList<int>();
//需要掌握LinkedList和LinkedListNode两个类
#endregion
#region 增
//尾部加
ints.AddLast(1);
//头部加
ints.AddFirst(2);
//在某节点前加,第一个值是寻找的节点,第二个值是新加数据的值
LinkedListNode<int> listNode = ints.Find(2);
ints.AddBefore(listNode, 10);
ints.AddAfter(listNode, 11);
#endregion
#region 删
//移除头
ints.RemoveFirst();
//移除尾部
ints.RemoveLast();
//移除指定数据节点
ints.Remove(1);
//清空
ints.Clear();
#endregion
#region 查
ints.AddLast(1);
//查头、尾节点
LinkedListNode<int> node = ints.First;
node = ints.Last;
//查找指定数据节点
node = ints.Find(1);
Console.WriteLine(node.Value);
//判断是否存在
ints.Contains(1);
#endregion
#region 改
//需要先得到再改
node.Value = 20;
#endregion
#region 遍历
//foreach遍历
Console.WriteLine("----------");
ints.AddLast(2);
ints.AddLast(214);
foreach (int item in ints)
{
Console.WriteLine(item);
}
#endregion
//遍历节点,也可以将ints.First改为ints.Last,这样就是从尾到头遍历。
LinkedListNode<int> temp = ints.First;
while(temp != null)
{
Console.WriteLine(temp.Value);
temp = temp.Next;
}
#region 练习
Console.WriteLine("练习开始:----------------");
//加入10个变量
LinkedList<int> list = new LinkedList<int>();
list.AddLast(89);
list.AddFirst(22);
list.AddBefore(list.Find(22), 10);
list.AddAfter(list.First, 82);
list.AddLast(781);
list.AddLast(1111);
list.AddLast(33);
list.AddLast(03);
list.AddLast(53);
list.AddLast(41);
list.Clear();
Random r = new Random();
for(int i = 0; i < 10; i++)
{
list.AddLast(r.Next(1, 3));
}
//遍历
foreach(int item in list)
{
Console.WriteLine(item);
}
//反向遍历
Console.WriteLine("反向遍历开始:----------");
temp = list.Last;
while(temp != null)
{
Console.WriteLine(temp.Value);
temp = temp.Previous;
}
#endregion
}
}
}
十一、总结容器与泛型栈、泛型队列
namespace Lesson11_泛型栈和队列
{
internal class Program
{
static void Main(string[] args)
{
#region 接触过的容器
/*值类型:
* 无符号的
* 1字节 2字节 4字节 8字节
* byte ushort uint ulong
* 有符号的
* 1字节 2字节 4字节 8字节
* sbyte short int long
* 4字节 8字节 16字节
* float double decimal
* 2字节 1字节
* char bool
* enum struct
*/
/*引用类型:
* string object interface class等
*/
/*Object型集合
* ArrayList object数据列表
* Stack 栈 先进后出
* Queue 队列 先进先出
* Hashtable 哈希表,键值对
*/
/*泛型数组
* List 泛型列表
* Dictionary 泛型键值对
* LinkedList 泛型链表
* Stack 泛型栈
* Queue 泛型队列
*/
#endregion
#region 泛型栈、队列
Stack<int> s = new Stack<int>();
Queue<Object> q = new Queue<object>();
#endregion
}
}
}
十二、委托
namespace Lesson12_委托
{
//委托可以用于将一系列方法按顺序加入,再之后触发。
delegate void MyFun1();
delegate void MyFun2<T>(T value);
delegate int MyFun3();
delegate void MyFun4(int i);
//委托不能重名,也不能重载
//存入的方法必须格式一致
//委托常用法:在类中使用,并在委托中可执行其他操作
class Test
{
MyFun1 m1;
MyFun4 m4;
public void test1(MyFun1 m1, MyFun4 m4)
{
this.m1 = m1;
this.m4 = m4;
}
}
internal class Program
{
static void Main(string[] args)
{
#region 委托基本概念
//委托是函数的容器可以理解为函数的变量类型
//关键字:delegate
//修饰符 delegate 返回值 委托名(参数列表);
//一般是写在namespace中,也能写在class中
//默认为public
#endregion
//存入委托
MyFun1 f10 = new MyFun1(Fun);
//直接调用
f10.Invoke();
//存入委托第二种方法:
MyFun1 f11 = Fun;
//调用
f11();
MyFun2<int> f21 = Fun2;
f21(5);
Test t = new Test();
t.test1(Fun, Fun2);
//存入多个函数
MyFun4 mf4 = Fun2;
mf4 += Fun2;
//移除函数
mf4 -= Fun2;
mf4(50);
//清空
mf4 = null;
mf4 += Fun2;
//mf4 -= Fun2;
//mf4 -= Fun2;
mf4(1);
#region 系统提供的委托
Action action = Fun;//无参无返回值委托
Func<int> fint = Fun3;//返回值为泛型的无参委托
Action<int> astring = Fun2;//参数(最多16个参数)为泛型无返回值类型。
Func<int, int> fint1;//<参数类型(最多16个 ,返回值类型 >最后一个为返回值类型。
//Action 管理无返回值委托。参数可0到16
//Func 管理有返回值, 也可有参数的类型,参数可以0到16。
#endregion
#region 练习1:模拟一家人吃饭
Action<string> aFamily1 = MakeFood;
Action<string, string> aFamily2 = PrepareFood;
Action<string, string, string> aFamily3 = EatFood;
aFamily1("Mother");
aFamily2("Mother", "Child");
aFamily3("Mother", "Child", "Father");
#endregion
#region 练习2:模拟击杀怪物
//更好的逻辑是:在怪物类中添加委托并把加钱、更新、增加成就三个方法加入其中,在死亡时触发委托。
Action<int> AddMoney = Fun2;
Action UpdateUI = Fun;
Action<int> AddNum = Fun2;
AddMoney(10);
UpdateUI();
AddNum(1);
#endregion
}
static void MakeFood(string m)
{
Console.WriteLine(m + "在做饭");
}
static void PrepareFood(string m, string c)
{
Console.WriteLine($"{m}和{c}开饭");
}
static void EatFood(string m, string c, string f)
{
Console.WriteLine($"{m}和{c}与{f}开饭");
}
public static void Fun()
{
Console.WriteLine("委托测试");
}
static void Fun2(int v)
{
Console.WriteLine(v);
}
static int Fun3()
{
return 3;
}
}
}
十三、事件
using System.Security.Cryptography;
namespace Lesson13_事件
{
class Test
{
#region 事件是什么
//事件是基于委托的存在,是一种特殊的变量类型
//事件是委托的安全包裹,让委托使用更具有安全性
#endregion
#region 事件声明和使用
//事件的声明和使用,事件不能再类外部声明,赋值,调用。
//事件的使用和委托一样
//事件不能作为临时变量在函数中声明
//事件的作用:防止外部随意声明空委托,防止随意调用委托。
/*总结:
* 事件与委托区别:事件是特殊委托,事件不能再外部使用= 和 声明,委托可以。
* 事件不能再外部执行,委托可以。
* 事件不能作为临时变量,委托可以。
*/
#region 练习1:热水器
event Action hot;
int temperature;
public void UseHot()
{
temperature = 0;
hot = AddHot;
hot += WarnHot;
hot += ShowHot;
hot();
}
public void AddHot()
{
Console.WriteLine("加热");
int i = 0;
while (true)
{
if (i % 9999999 == 0)
{
temperature++;
}
i++;
if (temperature > 100) return;
}
}
public void WarnHot()
{
if(temperature > 95)
{
Console.WriteLine("95摄氏度了");
}
}
public void ShowHot()
{
Console.WriteLine("水开了");
}
#endregion
public event Action Ac;
public Test()
{
Ac = Fun1;
Ac += Fun1;
Ac -= Fun1;
Ac();
}
#endregion
public static void Fun1()
{
Console.WriteLine("Fun1");
}
}
internal class Program
{
static void Main(string[] args)
{
Test t = new Test();
t.UseHot();
}
}
}
十四、匿名函数
namespace Lesson14_匿名函数
{
internal class Program
{
static void Main(string[] args)
{
#region 什么是匿名函数
//匿名函数是没有名字的函数。
//匿名函数必须配合委托和事件使用。
//匿名函数无法除去,只能= null来清空。
#endregion
#region 基础语法
/*delegate (参数列表)
* {
* //语句
* }
* 何时使用?
* 1.函数中传递委托参数时
* 2.委托或事件赋值时
*/
//使用:
Action action = delegate ()
{
Console.WriteLine("你好");
};
action();
//有参函数
Action<int> action1 = delegate (int a)
{
Console.WriteLine($"{a}你好");
};
action1(1);
//有返回值
Func<int> func = delegate ()
{
return 8;
};
Console.WriteLine(func());
//使用场景:
//1、参数传递
TestClass tc = new TestClass();
tc.Test1(delegate ()
{
Console.WriteLine("Test1");
});
//2、返回值传递
Action a = tc.Test2();
//或者
tc.Test2()();
#endregion
Console.WriteLine(tc.Cale(5)(4));
}
}
class TestClass
{
public void Test1(Action action)
{
action();
}
public Action Test2()
{
return delegate ()
{
Console.WriteLine("Test2");
};
}
public Func<int, int> Cale(int x)
{
return delegate (int y)
{
return x * y;
};
}
}
}
十五、lambad表达式
namespace Lesson15_lambad表达式
{
internal class Program
{
#region 练习题
static Action ac;
static Action Test()
{
for(int i = 1; i < 11; i++)
{
int index = i;
ac += () =>
{
Console.WriteLine(index);
};
}
return ac;
}
#endregion
static void Main(string[] args)
{
#region 什么是lambad表达式
//
/*语法:
* (参数列表) => {
* 语句;
* };
*/
#endregion
#region 使用
//无参无返回值
Action action = () =>
{
int i = 1;
Console.WriteLine(i);
};
//可以省略形参类型
Action<int> action1 = (value) =>
{
Console.WriteLine(value);
};
Fun2();
#endregion
action = Test();
action();
}
static event Action action2;
//闭包:内层使用外层的变量,延长了变量生命周期
static void Fun1()
{
int i = 1;
action2 = () =>
{
Console.WriteLine(i);
};
action2();
i++;
action2();
}
static void Fun2()
{
Fun1();
action2();
}
}
}
十六、List排序
namespace Lesson16_List排序
{
//7、练习
#region 练习
//对怪物类进行排序
class Monster1
{
public int atk;
public int def;
public int hp;
public Monster1(int atk, int def, int hp)
{
this.atk = atk;
this.def = def;
this.hp = hp;
}
}
#endregion
//3、自定义继承并实现方法
class Item : IComparable<Item>
{
public int money;
public Item(int money)
{
this.money = money;
}
public int CompareTo(Item? other)
{
//大于0则将当前对象放在传入other对象后面
if (this.money > other.money)
{
return 1;
}
else
{
return -1;
}
}
}
class Monster
{
public int id;
public Monster(int id)
{
this.id = id;
}
}
internal class Program
{
static void Main(string[] args)
{
#region 1、List自带排序
//list.Sort();在ArrayList中也有这个方法。
List<int> ints = new List<int>();
ints.Add(2);
ints.Add(4);
ints.Add(90);
ints.Add(1);
foreach (int item in ints)
{
Console.WriteLine(item);
}
Console.WriteLine("-------------");
ints.Sort();
foreach (int item in ints)
{
Console.WriteLine(item);
}
#endregion
#region 2、自定义排序
//类的排序 需要继承IComparable
Console.WriteLine("-----自定义排序---------");
List<Item> items = new List<Item>();
items.Add(new Item(100));
items.Add(new Item(29));
items.Add(new Item(4));
items.Add(new Item(8));
items.Add(new Item(25));
items.Add(new Item(19));
items.Sort();//验证自定义Item类排序
foreach (Item item1 in items)
{
Console.WriteLine(item1.money);
}
Console.WriteLine("--------重载自定义排序------------");
List<Monster> mls = new List<Monster>();
mls.Add(new Monster(123));
mls.Add(new Monster(45));
mls.Add(new Monster(5));
mls.Add(new Monster(79));
mls.Add(new Monster(22));
//5、还能用lambad表达式来写匿名函数。
//mls.Sort((m1, m2) =>
//{
// if (m1.id > m2.id)
// {
// return 1;
// }
// else
// {
// return -1;
// }
//});
mls.Sort((m1, m2) => { return m1.id > m2.id ? 1 : -1; });//6、运用lambad表达式和三目运算符
//mls.Sort(Compare)//重载方法测试
foreach (Monster ml in mls)
{
Console.WriteLine(ml.id);
}
//8、练习测试
List<Monster1> mons = new List<Monster1>();
for (int i = 0; i < 10; i++)
{
mons.Add(new Monster1(10 + i, 5 + i, 10 + i * 5));
}
Console.WriteLine("开始练习测试");
int playerNum = Console.Read();
Console.WriteLine($"你输入的是:{playerNum}");
if (playerNum == 1)
{
mons.Sort((m1, m2) => { return m1.atk > m2.atk ? 1 : -1; });
}
else if(playerNum == 2)
{
mons.Sort((m1, m2) => { return m1.def > m2.def ? 1 : -1; });
}
else if (playerNum == 3)
{
mons.Sort((m1, m2) => { return m1.hp > m2.hp ? 1 : -1; });
}
else if(playerNum == 4)
{
int num;
List<Monster1> temp1 = new List<Monster1>();
for(int i = 0; i < mons.Count; i++)
{
num = mons.Count - i - 1;
temp1.Add(mons[num]);
}
mons = temp1;
foreach (Monster1 item in mons)
{
Console.WriteLine(item.atk);
}
}
else
{
Console.WriteLine("请重新输入");
}
#endregion
//4、自定义重载方法:需要利用Sort的重载方法传入一个排序方法
static int Compare(Monster m1, Monster m2)
{
if (m1.id > m2.id)
{
return 1;
}
else
{
return -1;
}
}
}
}
}
十七、协变逆变
#region 作用:
//参数和返回值
delegate void Go<in T>(T t);
delegate T Fly<out T>();
interface Test1<out T>
{
T Fun1();
}
#endregion
//结合里式替换原则进行
TestOut<Son> os = () =>
{
return new Son();
};
TestOut<Father> of = os;
Father father = of();
TestIn<Father> If = (f) => {};
TestIn<Son> Is = If;
Is(new Son());
十八、多线程
//注意:声明的lock锁的判定变量必须是引用类型
internal class Program
{
static bool isRun = true;
delegate void Customize();
static void Main(string[] args)
{
Customize customize = () =>
{
Console.WriteLine("OK1");
};
Thread td1 = new Thread(new ThreadStart(customize));
Thread td2 = new Thread(() => { Console.WriteLine("OK2"); });
Thread td3 = new Thread(FunWhile);
//开启线程
td1.Start();
td2.Start();
td3.Start();
//设置成后台线程,受主线程
td1.IsBackground = true;
//1、需要再主程序中控制线程是否关闭,可以加如bool标识来控制。
//isRun = false;
//2、中止程序
//td3.Abort();//已经废弃
//td3 = null;
}
static void FunWhile()
{
while (isRun)
{
Console.WriteLine("循环3");
//线程休眠
Thread.Sleep(2000);
}
}
static object conlock = new object();
static void Main(string[] args)
{
Thread thread = new Thread(Fun1);
thread.Start();
while (true)
{
lock(conlock){
Console.SetCursorPosition(0, 0);
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("*");
}
}
}
static void Fun1()
{
while (true)
{
lock (conlock)
{
Console.SetCursorPosition(10, 0);
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("□");
}
}
}
}
十九、预处理器指令
//定义符号
#define unity4
//取消符号
#undef unity4
namespace L36_预处理器指令
{
internal class Program
{
static void Main(string[] args)
{
//if处理,判断有没有该符号,有则会被编译;还能加||和&&
#if unity4
Console.WriteLine("Hello");
#elif unity4
Console.WriteLine("123")
#else
Console.WriteLine("456");
#endif
//警告和错误
#warning 不好
#error 出错啦
}
}
}
二十、反射概念和关键类Type
需要引入命名空间using System.Reflection;
# region 获取公共构造函数
//获取所有公共构造函数
ConstructorInfo[] constructors = t2.GetConstructors();
foreach(ConstructorInfo con in constructors)
{
Console.WriteLine(con);
}
//得到构造函数传入 Type数组 数组中按顺序是参数类型
//执行构造函数,传入object数组,按顺序是参数
//无参构造
ConstructorInfo info = t2.GetConstructor(new Type[0]);
Console.WriteLine(info);
Test test2 = info.Invoke(null) as Test;
Console.WriteLine(test2.y);
//有参构造
ConstructorInfo info1 = t2.GetConstructor(new Type[] {typeof(int)});
Test test1 = info1.Invoke(new object[] { 3 }) as Test;
Console.WriteLine(test1.x);
#endregion
#region 获取公共成员变量
//获取所有公共成员变量
Console.WriteLine("获取所有公共成员变量----");
FieldInfo[] fieldInfo = t.GetFields();
for (int i = 0; i < fieldInfo.Length; i++)
{
Console.WriteLine(fieldInfo[i]);
}
//得到指定变量
FieldInfo field = t.GetField("x");
Console.WriteLine(field);
//获取变量值
object obj = info.Invoke(null);
object tempNum = field.GetValue(obj);
Console.WriteLine($"tempNum:{tempNum}");
//设置变量值
field.SetValue(obj, 10);
Console.WriteLine("x:" + field.GetValue(obj));
#endregion
#region 获取公共成员方法
//获取所有公共成员方法
Console.WriteLine("公共成员方法------");
Type type = typeof(int);
MethodInfo[] methods = type.GetMethods();
for (int i = 0; i < methods.Length; i++)
{
Console.WriteLine($"methods[{i}]: {methods[i]}");
}
//得到公共成员方法
MethodInfo method = type.GetMethod("Parse", new Type[] { typeof(string) });
//使用(注意,第一个参数是哪个对象要执行,如果是静态方法传入null)
int num = (int)method.Invoke(null, new object[] {"123"});
Console.WriteLine("num类型是:" + num);
#endregion
二十一、反射关键类Activator和Assembly
#region Activator
Console.WriteLine("----Activator----");
//用于快速实例化对象的类
Type acType = typeof(Test);
//实例化无参
Test acTest = Activator.CreateInstance(acType) as Test;
Console.WriteLine(acTest.x);
//有参构造
Test acTest1 = Activator.CreateInstance(acType, 12) as Test;
Console.WriteLine(acTest1.x);
#endregion
#region Assembly
Console.WriteLine("-------Assembly----------");
//加载指定程序集
Assembly assembly =
Assembly.LoadFrom(@"E:\temp\Csharp\CSharp进阶\L36_预处理器指令\bin\Debug\net8.0\L36_预处理器指令");
Type[] types = assembly.GetTypes();
foreach (Type item in types)
{
Console.WriteLine(item);
}
//加载其中一个类,填写命名空间.类名(注意:如果没写在命名空间中,前面不填)
Console.WriteLine("-----使用加载物中的类-----");
Type type1 = assembly.GetType("TestMonster");
MemberInfo[] memberInfo = type1.GetMembers();
for (int i = 0; i < memberInfo.Length; i++)
{
Console.WriteLine(memberInfo[i]);
}
//获取枚举
Type type2 = assembly.GetType("E_Type");
MemberInfo[] E_infos = type2.GetMembers();
Console.WriteLine("-----打印枚举信息------");
foreach (MemberInfo item in E_infos)
{
Console.WriteLine(item);
}
FieldInfo ty1 = type2.GetField("type1");
//使用TestMonster带有枚举的构造方法
object tm = Activator.CreateInstance(type1, ty1.GetValue(null), 5, 10);
Console.WriteLine("----打印传入字符-------");
MethodInfo methodInfo = type1.GetMethod("Hello", new Type[] {typeof(string)});
//传入对象是实例对象, 和参数
methodInfo.Invoke(tm, new object[] {"你好"});
#endregion
二十二、特性
英语生词:usage:使用。Targets:目标。Multiple:倍数,多个。Inherited:继承的。Attribute:属性。
namespace L43_特性
{
//为特性添加特性的限制
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = true, Inherited = false)]
class MyCustomAttribute : Attribute
{
public string Name { get; set; }
public MyCustomAttribute(string name)
{
Name = name;
}
}
//类或其他元数据上写特性,[特性名(参数)]
[MyCustom("你好,这是一个测试类")]
[MyCustom("世界")]
public class MyTest
{
public void PrintStr([MyCustom("变量特性")] string str)
{
}
}
internal class Program
{
static void Main(string[] args)
{
Type t = Type.GetType("L43_特性.MyTest");
//判断是否使用了该特性 参数一:特性类型,并参数二:查找父类是否使用了。
if (t.IsDefined(typeof(MyCustomAttribute), false))
{
Console.WriteLine("使用了该特性");
}
//获取并打印
object[] Attributes = t.GetCustomAttributes(true);
foreach (object attr in Attributes)
{
Console.WriteLine((attr as MyCustomAttribute).Name);
}
}
}
}
英语生词:Obsolete:过时的。
英语生词:Caller:呼叫者
public class MyTest
{
[Obsolete("该方法已经过时啦----")]
public void PrintStr([MyCustom("变量特性")] string str, [CallerFilePath] string x = "")
{
Console.WriteLine($"{str}和{x}");
}
}
在主函数中输出:
MyTest test = new MyTest();
test.PrintStr("123");
英语生词:Conditional:有条件的。define:定义
在最上方:
#define Fun
public class MyTest
{
[Conditional("Fun")]
public void Fun()
{
Console.WriteLine("Fun输出");
}
}
在主函数中:
MyTest test = new MyTest();
test.Fun();
二十三、迭代器
using System.Collections;
namespace L23_迭代器
{
class Level : IEnumerator, IEnumerable
{
int[] arr;
//用于计数的下标
int nextIndex;
public Level()
{
arr = new int[] { 9, 6, 3, 10, 21 };
nextIndex = -1;
}
//获取当前值
public object Current
{
get
{
return arr[nextIndex];
}
}
public IEnumerator GetEnumerator()
{
return new Level();
}
//用于判断下一位置是否有值
public bool MoveNext()
{
nextIndex++;
return nextIndex < arr.Length;
}
//一般用于重置光标
public void Reset()
{
throw new NotImplementedException();
}
}
internal class Program
{
static void Main(string[] args)
{
Level level = new Level();
foreach(int i in level)
{
Console.WriteLine(i);
}
foreach (int i in level)
{
Console.WriteLine(i);
}
}
}
}
class MonsterLevel : IEnumerable
{
int[] arrs = { 9, 6, 3, 1 };
public MonsterLevel()
{
}
public MonsterLevel(int[] arrs)
{
this.arrs = arrs;
}
public IEnumerator GetEnumerator()
{
for (int i = 0; i < arrs.Length; i++) {
yield return arrs[i];
}
}
}
二十四、特殊语法
知识二:初始化
class Monster
{
int money;
public string name;
public int id
{
get; set;
}
public Monster(int id) {
this.id = id;
}
}
internal class Program
{
static void Main(string[] args)
{
Monster monster = new Monster(2) { name = "123"};
Console.WriteLine(monster.name + monster.id);
}
}
知识四:匿名类型
var con = new {name = "战士"};
Console.WriteLine(con.name);
知识五:可空类型
#region 知识五:可空类型
//值类型不能为空。但在类型后面加?可以赋值为null
int? x = null;
//判断是否为空,空就不进入
if (x.HasValue)
{
Console.WriteLine("进入");
Console.WriteLine(x.Value);
}
//安全获取可空类型:为空返回该类型默认值,也可后面给值返回,不会将给的值赋值给获取变量
Console.WriteLine(x.GetValueOrDefault(100));
//调用是判断是否为空,不为空就输出
string str = "测试";
Console.WriteLine(str?.ToString());
int[] ints = null;
Console.WriteLine(ints?.Length);
#endregion
Console.WriteLine(str ?? "1223");
二十五、值和引用类型
TestStruct(结构体)、ITest(接口)在1中打印 1 1 2 因为在栈中声明变量会新开一个房间用于存放而2中因为存在装箱将栈转换为堆中,所以打印99