C#实战附俄罗斯方块实战

C#实战

ArrayList

using System;
using System.Collections;
using System.Security.Principal;
namespace ArrayList数组;
class Program
{
    static void Main(string[] args)
    {
        #region 本质
        /* ArrayList是一个C#封装好的类
             本质是一个object类型的数组
             ArrayList,*/
        #endregion
        #region 声明
        //需要引用 命名空间using System.Collections;
        ArrayList array = new ArrayList();
        #endregion
        #region 增删查改
        array.Add(1);
        array.Add("");
        array.Add(true);
        int v = array.Add(new object());
        array.Add(new Test());
        // 把另一个容器增加到另一个容器
        array.AddRange(array);

        //删
        //指定元素
        array.Remove(1);
        //指定位置
        array.RemoveAt(0);
        array.RemoveAt(1);
        //清空
        //array.Clear();

        // 查
        Console.WriteLine(array[0]);
        //查看元素是否存在
        if (array.Contains("1"))
        {

        }
        // 正向查找元素位置
        //找到返回值
        int index = array.IndexOf("1");
        Console.WriteLine(index);
        // 反向查找元素
        //int index2 = array.IndexOf(true);
        index = array.LastIndexOf(true);
        Console.WriteLine(index);
        // 改
        Console.WriteLine(array[0]);
        array[0] = "1"; 
        Console.WriteLine(array[0]);
        #endregion

        #region 遍历
        //长度
        Console.WriteLine(array.Count);
        // 避免产生过多垃圾
        Console.WriteLine(array.Capacity);
        for (int i = 0; i < array.Count; i++)
        {
            Console.WriteLine(array[i]);
        }
        foreach (var item in array)
        {
            Console.WriteLine(item);
        }
        #endregion
        #region 装箱拆箱
        // ArrayList本质上可以自动扩容object数组
        int i2 = 1;
        array[0] = i2;// 装箱
        i2 = (int)array[0];
        Console.WriteLine(i2);
        #endregion
    }
}

Stack

using System;
using System.Collections;
namespace bascicprograme;

class Test { }
class Program
{
    static void Main(string[] args)
    {
        #region stack本质
        //stack是一个C#封装好的类
        /* 它的本质是object数组,只是封装了特殊的存储规则
         Stack是栈存储容器,栈是一种先进后出的数据结构
         先存入的数据后取出,后存入的数据先取出*/

        #endregion
        #region 声明
        Stack values = new Stack();
        #endregion
        #region 增取改查
        //压栈
        values.Push(1);
        values.Push(true);
        values.Push("1111");
        values.Push(new Test());
        #endregion

        #region 取
        //栈中不存在删除的概念
        //弹栈

        object v =  values.Pop();
        Console.WriteLine(v);

        #endregion
        #region 查
        //1、栈无法查看指定位置元素
            //只能查看栈顶内容
            //2、查看元素是否存在于栈中
            v = values.Peek();
        Console.WriteLine(v);
        #endregion
        #region 改
        //栈无法改变其中的元素,只能压和弹
            //只能清空
            //values.Clear();

        #endregion
        #region 遍历
        Console.WriteLine(values.Count);
        //foreach遍历
        foreach (object item in values)
        { 
            Console.WriteLine(item);
        }
        //另一种遍历方式
        //将栈转换为object数组
        //遍历出来顺序也是从栈顶到栈底
        object[] arr = values.ToArray();
        for (int i = 0; i < arr.Length; i++)
        {
            Console.WriteLine(arr[i]);

        }
        // 循环弹栈
        while (values.Count>0)
        { 
            object o = values.Pop();
            Console.WriteLine(o);
        }
        Console.WriteLine(values.Count);
           
    
        #endregion


    }


}

Queue

using System;
using System.Collections;
namespace 队列;
class Test {


}
class Program
{
    static void Main(string[] args)
    {
        #region 队列本质
        /*queen是一个封装好的
            本质也是object数组只是封装了特殊的规则

            queue是对类存储容器
            队列是一种新进先出的数据结构
            先存入的数据先获取后存入的后获取*/

        #endregion
        #region 声明
        Queue queue = new Queue();
        #endregion

        #region 增取查改
        // 增
        queue.Enqueue(1);
        queue.Enqueue("2");
        queue.Enqueue(true);
        // 取
        object o = queue.Dequeue();
        Console.WriteLine(o);
        Console.WriteLine(queue.Dequeue());
        // 查
        // 查看头部元素但不会移除
        o = queue.Peek();
        Console.WriteLine(o);

        // 查看元素是否存在队列中
        if (queue.Contains("3"))
        {
            Console.WriteLine("不存在");
        }
        // 改
        //只能清空,改不了
        //queue.Clear();
        queue.Enqueue(1);
        // 遍历
        //
        foreach (var item in queue)
        {
            Console.WriteLine(item);
        }
        object[] arr = queue.ToArray();
        for (int i = 0; i < arr.Length; i++)
        {
            Console.WriteLine(arr[i]);
        }
        // 循环出列
        while (queue.Count>0)
        {
            o = queue.Dequeue();
            Console.WriteLine(o);

        }
        #endregion
    }
}

HashTable

using System;
using System.Collections;
namespace 哈希表练习题;

//制作一个怪物管理器,提供创建怪物
//移除怪物的方法。每个怪物都有自己的唯一ID

/// <summary>
/// 怪物管理器 因为一般 管理器 都是唯一的 所以把它做成 一个单例模式的对象
/// </summary>

// 泛型单例
/*public class Singleton<T> where T : class, new()
{
    // 私有静态实例
    private static T instance;
    // 用于线程同步的锁对象
    private static readonly object lockObject = new object();

    // 公共的静态属性,用于获取唯一实例
    public static T Instance
    {
        get
        {
            // 双重检查锁定,提高性能
            if (instance == null)
            {
                lock (lockObject)
                {
                    if (instance == null)
                    {
                        instance = new T();
                    }
                }
            }
            return instance;
        }
    }
}

// 具体的单例类,继承自泛型单例基类
public class MySingleton : Singleton<T>
{
    // 可以在这里添加MySingleton特有的方法和属性
    private MySingleton() { }
}*/
class MonsterMgr
{ 
    private static MonsterMgr instance = new MonsterMgr();

    private Hashtable monstersTable = new Hashtable();
    private MonsterMgr() { }
    public static MonsterMgr Instance
    { 
        get { return instance; }
    }
    private int monsterId = 0;
    public void AddMonster (){ 
    
        Monster monster = new Monster(monsterId);
        Console.WriteLine("创建了{0}的怪物",monsterId);
        ++monsterId;
        monstersTable.Add(monster.id,monster);

    }
    public void RemoveMonster(int monserID)
    {
        if (monstersTable.ContainsKey(monserID)) {
            (monstersTable[monserID] as Monster).Dead();
            monstersTable.Remove(monserID);
        
        }
    }

}

class Monster
{ 
    public int id;
    public Monster(int id) { 
        this.id = id;
    }
    public void Dead()
    {
        Console.WriteLine("怪物{0}死亡",id);
    }

}
class Program
{
    static void Main(string[] args)
    {
        MonsterMgr.Instance.AddMonster();
        MonsterMgr.Instance.AddMonster();
        MonsterMgr.Instance.AddMonster();
        MonsterMgr.Instance.AddMonster();
        MonsterMgr.Instance.AddMonster();

        MonsterMgr.Instance.RemoveMonster(0);
        MonsterMgr.Instance.RemoveMonster(1);
        MonsterMgr.Instance.RemoveMonster(2);


    }
}

泛型

using System;
using System.Security.Cryptography;
namespace 泛型;
#region 泛型是什么
// 泛型实现了类型参数化,达到代码重用的目的
//通过类型参数化实现同一份代码操作多种类型
// 泛型相当于类型占位符
// 定义类或方法时使用替代付代表变量类型
// 当真正使用类或者方法时再具体指定类型
#endregion
#region 泛型分类
//基本语法
//class 类名<泛型占位字母>

// 泛型函数
//基本语法
//函数名 <泛型占位字母>(参数)
#endregion

class TestClass<T> {
    public T Value;
}

class TestClass2<T, A, B, C> { 
   public T Value;
    public A Value1;
    public B Value5;
    public C Value6;
    
};

interface IClass<T> {
    T Value { 
        get; set;
    
    }
}
// 继承
class Test : IClass<string>
{
    public string Value { get; set; }
}

// 泛型方法

class TestFun { 
   public void Test<T> (T value) { 
        Console.WriteLine (value);
    }
    public void Test2<T>(){ 
        T t =default (T);
        Console.WriteLine (t);
    }
    public T Test3<T>(string V) {
        return default(T);
    }
    public void Test<T,K,M>(T t, K k ,M m) { }
}

class TestFun<T> {
    public T Value;
    // 我们这个不叫泛型方法,T是泛型声明时候就制定在使用这个函数时候就不能再动态的变化了
    // 没加<>不是泛型
    public void Test(T t) { 
    
    }
}

// 泛型作用
/*不同类型对象相同逻辑处理就可以选择泛型
    使用泛型就可以一定程度上避免装箱拆箱*/


class ArrayList<T>
{ 
    private T[] array;
    private void Add(T value) { 
        
    }
    private void Clear()
    {
    }
    private void Remove() { 
    
    
    }

}
class Program
{
    static void Main(string[] args)
    {
        TestClass<int>  t = new TestClass<int>();   
        t.Value = 1;
        Console.WriteLine(t.Value);

        TestClass<String> testClass = new TestClass<String>();
        testClass.Value = "555555555";
        Console.WriteLine(testClass.Value);

        TestClass2<int, string, float, double> testClass2 = new TestClass2<int, string, float, double>() { }; 

        TestFun fun = new TestFun();
        fun.Test<string>("123");

        TestFun<int> fun2 = new TestFun<int>();   
        fun2.Test(11);
       
    }
}

泛型约束

using System;
using static System.Net.Mime.MediaTypeNames;
namespace 泛型的约束;

#region 什么是泛型约束
/*让泛型的类型有一定限制
    关键字where
    泛型约束一共有六种
    值类型 where 泛型字母:struct
    引用类型  where 泛型字母:class
    存在无参公共构造函数  where 泛型字母:new()
    某个类本身或者其派生类  where 泛型字母:类名
    某个接口派生类型  where 泛型字母:接口名
    另一个泛型类型本身或者派生类型  where 泛型字母:另一个泛型字母
*/
#endregion

#region 值类型约束
class Test1<T> where T : struct
{
    public T Value;
    public void TestFun<T>(T t) where T : struct { 
    
    }
}
class Test2<T> where T : class
{
    public T Value;
    public void TestFun<T>(T t) where T : class
    {

    }
}
#endregion

#region 公共无参构造函数
class Test3<T> where T : new()
{
    public T Value;
    public void TestFun<T>(T t) where T : new()
    {

    }
}


#endregion
#region 类约束
class Test4<T> where T : Test1
{
    public T Value;
    public void TestFun<T>(T t) where T : Test1
    {

    }
}

class Test3 : Test1
{ 

    
}
#endregion

#region 接口约束
interface Ifly { }

class Test5 : Ifly { 

}

class Test6<T> where T : Ifly
{
    public T Value;
    public void TestFun<T>(T t) where T : Ifly
    {

    }
}

#endregion
#region 另一个泛型约束
//U是T的派生类
class Test7<T, U> where T : U {
    public T Value;
    public void TestFun<T>(T t) where T : U
    {

    }

}

#endregion

#region 约束的组合使用
class Test8<T> where T :class,new()
{
    public T Value;
    public void TestFun<T>(T t) where T : class, new()
    {
        
    }
}
#endregion

#region 多个泛型有约束
class Test9<T,K> where T : class, new() where K : class
{
    public T Value;
    public void TestFun<T,K>(T t , K k) where T : class, new() where K : class
    {

    }
}
#endregion
class Program
{
    static void Main(string[] args)
    {
        Test1<int> t = new Test1<int>();
        t.TestFun<float>(1.3f);
        Test2<object> t1 = new Test2<object>();

        Test2<Random> t2 = new Test2<Random>();
        t2.Value = new Random();
        t2.TestFun<object>(new object());
        Test4<Test3> test4 = new Test4<Test3>();
        Test4<Test1> test5 = new Test4<Test1>();

        Test6<Ifly> t5 = new Test6<Ifly>();
        t5.Value = new Test5();
        Test7<Test5, Ifly> t7 = new Test7<Test5, Ifly>();

    }
}
   



List

namespace List链表;
class Program
{
    static void Main(string[] args)
    {
        #region List本质
        //List#封装好的,本质是一个可变类型的泛型数组
        //List类帮助我们实现很多方法,比如泛型数组的增删改查


        #endregion
        #region 声明
        //需要引用命名空间
        List<string> list = new List<string>();
        list.Add("1");
        list.Add("1");
        List<int> list2 = new List<int>();  
        List<int> list3= new List<int>();
        list2.Add(2);
        list2.Add(2);
        list2.Add(2);
        list.AddRange(list);
        list2.AddRange(list3);

        // 删
        list.Remove("1");
        list.RemoveAt(0);
        // 查
       Console.WriteLine( list[0]);
        if (list.Contains("1"))
        {
            Console.WriteLine("存在"); 
        }
        int index = list.IndexOf("1");
        Console.WriteLine( index); // 找不到-1

        // 改
        Console.WriteLine(list[0]="333");
        list.Insert(0,"9999");
        Console.WriteLine(list[0]);

        // 遍历
        for (int i = 1; i < list.Count; i++)
        {
            Console.WriteLine(list[i]);
        }
        Console.WriteLine(list.Capacity);

        #endregion
    }
}

Dictionary

using System;
using System.Collections.Generic;
using System.IO;
namespace Dictionary字典;
class Program
{
    static void Main(string[] args)
    {
        #region Dictionary
        /*可以将Dictionary理解为泛型的哈希table
            他也是基于键的哈希代码组织起来的
            键值对类型从哈希的object变为了自己可以制定的泛型*/


        #endregion
        #region 声明
        Dictionary<int, string> directory = new Dictionary<int, string>();

        #endregion
        #region 增删改查
        // 注意不能出现相同键
        directory.Add(1,"1");
        directory.Add(2,"22");
        directory.Add(3,"333");
        directory.Add(4,"4444");
        directory.Add(5,"55555");
        directory.Add(6,"666666");

        // 删除
        directory.Remove(1);
        //清空
        //directory.Clear();
        //查
       
        Console.WriteLine(directory[2]);
        // 找不到不会返回空,直接报错

        // 查看是否存在
        if (directory.ContainsKey(1))
        { 
            Console.WriteLine("存在");
        }
        if (directory.ContainsValue("22"))
        { 
            Console.WriteLine("存在");

        }


        // 改
        Console.WriteLine(directory[2]="5555");
        //遍历
        foreach (var item in directory.Keys)
        {
            Console.WriteLine(item);
            Console.WriteLine(directory[item]);
        }

        foreach (var item in directory.Values)
        { 
            Console.WriteLine(item);
        }

        foreach (KeyValuePair<int,string> item in directory)
        { 
            Console.WriteLine("键"+item.Key+"值"+item.Value);
        }
        #endregion


    }
}


image-20250114185439821

顺序存储和链式存储

using System;

namespace Lesson9_顺序存储和链式存储
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("顺序存储和链式存储");

            #region 知识点一 数据结构 
            //数据结构
            //数据结构是计算机存储、组织数据的方式(规则)
            //数据结构是指相互之间存在一种或多种特定关系的数据元素的集合
            //比如自定义的一个 类 也可以称为一种数据结构 自己定义的数据组合规则

            //不要把数据结构想的太复杂
            //简单点理解,就是人定义的 存储数据 和 表示数据之间关系 的规则而已

            //常用的数据结构(前辈总结和制定的一些经典规则)
            //数组、栈、队列、链表、树、图、堆、散列表
            #endregion

            #region 知识点二 线性表
            //线性表是一种数据结构,是由n个具有相同特性的数据元素的有限序列
            //比如数组、ArrayList、Stack、Queue、链表等等
            #endregion

            //顺序存储和链式存储 是数据结构中两种 存储结构

            #region 知识点三 顺序存储
            //数组、Stack、Queue、List、ArrayList —— 顺序存储
            //只是 数组、Stack、Queue的 组织规则不同而已
            //顺序存储:
            //用一组地址连续的存储单元依次存储线性表的各个数据元素
            #endregion

            #region 知识点四 链式存储
            //单向链表、双向链表、循环链表 —— 链式存储
            //链式存储(链接存储):
            //用一组任意的存储单元存储线性表中的各个数据元素
            #endregion

            LindedList<int> link = new LindedList<int>();
            link.Add(1);
            link.Add(2);
            link.Add(3);
            link.Add(4);
            LinkedNode<int> node = link.head;
            while(node != null)
            {
                Console.WriteLine(node.value);
                node = node.nextNode;
            }
            link.Remove(2);
            node = link.head;
            while (node != null)
            {
                Console.WriteLine(node.value);
                node = node.nextNode;
            }

            link.Remove(1);
            node = link.head;
            while (node != null)
            {
                Console.WriteLine(node.value);
                node = node.nextNode;
            }
            link.Add(99);
            node = link.head;
            while (node != null)
            {
                Console.WriteLine(node.value);
                node = node.nextNode;
            }
        }
    }

    #region 知识点五 自己实现一个最简单的单向链表
    /// <summary>
    /// 单向链表节点
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class LinkedNode<T>
    {
        public T value;
        //这个存储下一个元素是谁 相当于钩子
        public LinkedNode<T> nextNode;

        public LinkedNode(T value)
        {
            this.value = value;
        }
    }

    /// <summary>
    /// 单向链表类 管理 节点 管理 添加等等
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class LindedList<T>
    {
        public LinkedNode<T> head;
        public LinkedNode<T> last;

        public void Add(T value)
        {
            //添加节点 必然是new一个新的节点
            LinkedNode<T> node = new LinkedNode<T>(value);
            if( head == null )
            {
                head = node;
                last = node;
            }
            else
            {
                last.nextNode = node;
                last = node;
            }
        }

        public void Remove(T value)
        {
            if( head == null )
            {
                return;
            }
            if( head.value.Equals(value) )
            {
                head = head.nextNode;
                //如果头节点 被移除 发现头节点变空
                //证明只有一个节点 那尾也要清空
                if( head == null )
                {
                    last = null;
                }
                return;
            }
            LinkedNode<T> node = head;
            while(node.nextNode != null)
            {
                if( node.nextNode.value.Equals(value) )
                {
                    //让当前找到的这个元素的 上一个节点
                    //指向 自己的下一个节点
                    node.nextNode = node.nextNode.nextNode;
                    break;
                }
            }
        }
    }
    #endregion

    #region 知识点六 顺序存储和链式存储的优缺点
    //从增删查改的角度去思考
    //增:链式存储 计算上 优于顺序存储 (中间插入时链式不用像顺序一样去移动位置)
    //删:链式存储 计算上 优于顺序存储 (中间删除时链式不用像顺序一样去移动位置)
    //查:顺序存储 使用上 优于链式存储 (数组可以直接通过下标得到元素,链式需要遍历)
    //改:顺序存储 使用上 优于链式存储 (数组可以直接通过下标得到元素,链式需要遍历)
    #endregion
}

LinkedList

using System;
using System.Collections.Generic;

namespace Lesson10_LinkedList
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("LinkedList");

            #region 知识点一 LinkedList
            //LinkedList是一个C#为我们封装好的类
            //它的本质是一个可变类型的泛型双向链表
            #endregion

            #region 知识点二 申明
            //需要引用命名空间
            //using System.Collections.Generic
            LinkedList<int> linkedList = new LinkedList<int>();
            LinkedList<string> linkedList2 = new LinkedList<string>();
            //链表对象 需要掌握两个类
            //一个是链表本身 一个是链表节点类LinkedListNode
            #endregion

            #region 知识点三 增删查改

            #region 增
            //1.在链表尾部添加元素
            linkedList.AddLast(10);

            //2.在链表头部添加元素
            linkedList.AddFirst(20);

            //3.在某一个节点之后添加一个节点
            //  要指定节点 先得得到一个节点
            LinkedListNode<int> n = linkedList.Find(20);
            linkedList.AddAfter(n, 15);
            //4.在某一个节点之前添加一个节点
            //  要指定节点 先得得到一个节点
            linkedList.AddBefore(n, 11);
            #endregion

            #region 删
            //1.移除头节点
            linkedList.RemoveFirst();
            //2.移除尾节点
            linkedList.RemoveLast();
            //3.移除指定节点
            //  无法通过位置直接移除
            linkedList.Remove(20);
            //4.清空
            linkedList.Clear();

            linkedList.AddLast(1);
            linkedList.AddLast(2);
            linkedList.AddLast(3);
            linkedList.AddLast(4);
            #endregion

            #region 查
            //1.头节点
            LinkedListNode<int> first = linkedList.First;
            //2.尾节点
            LinkedListNode<int> last = linkedList.Last;
            //3.找到指定值的节点
            //  无法直接通过下标获取中间元素
            //  只有遍历查找指定位置元素
            LinkedListNode<int> node = linkedList.Find(3);
            Console.WriteLine(node.Value);
            node = linkedList.Find(5);
            //4.判断是否存在
            if( linkedList.Contains(1) )
            {
                Console.WriteLine("链表中存在1");
            }
            #endregion

            #region 改
            //要先得再改 得到节点 再改变其中的值
            Console.WriteLine(linkedList.First.Value);
            linkedList.First.Value = 10;
            Console.WriteLine(linkedList.First.Value);
            #endregion

            #endregion

            #region 知识点四 遍历
            //1.foreach遍历
            foreach (int item in linkedList)
            {
                Console.WriteLine(item);
            }

            //2.通过节点遍历
            //  从头到尾
            Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&");
            LinkedListNode<int> nowNode = linkedList.First;
            while (nowNode != null)
            {
                Console.WriteLine(nowNode.Value);
                nowNode = nowNode.Next;
            }

            //  从尾到头

            Console.WriteLine("&&&&&&&&&&&&&&&&&&&&&&&&&&&");
            nowNode = linkedList.Last;
            while (nowNode != null)
            {
                Console.WriteLine(nowNode.Value);
                nowNode = nowNode.Previous;
            }

            #endregion
        }
    }
}

泛型栈和队列

using System;
using System.Collections.Generic;

namespace Lesson11_泛型栈和队列
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("泛型栈和队列");

            #region 知识点一 回顾数据容器

            #region 变量
            //无符号
            //byte ushort uint ulong
            //有符号
            //sbyte short int long
            //浮点数
            //float double decimal
            //特殊
            //char bool string
            #endregion

            #region 复杂数据容器
            //枚举 enum
            //结构体 struct
            //数组(一维、二维、交错) []  [,]  [][]
            //类
            #endregion

            #region 数据集合
            //using System.Collections;

            //ArrayList  object数据列表
            //Stack 栈  先进后出
            //Queue 队列  先进先出
            //Hashtable   哈希表  键值对
            #endregion

            #region 泛型数据集合
            //using System.Collections.Generic;

            //List  列表  泛型列表
            //Dictionary 字典  泛型哈希表
            //LinkedList 双向链表 
            //Statck 泛型栈
            //Queue 泛型队列
            #endregion

            #endregion

            #region 知识点二 泛型栈和队列
            //命名空间:using System.Collections.Generic;
            //使用上 和之前的Stack和Queue一模一样
            Stack<int> stack = new Stack<int>();
            Queue<object> queue = new Queue<object>();
            #endregion
        }
    }
}

委托

using System;

namespace Lesson12_委托
{
    #region 知识点一 委托是什么
    //委托是 函数(方法)的容器 
    //可以理解为表示函数(方法)的变量类型
    //用来 存储、传递函数(方法)
    //委托的本质是一个类,用来定义函数(方法)的类型(返回值和参数的类型)
    //不同的 函数(方法)必须对应和各自"格式"一致的委托
    #endregion

    #region 知识点二 基本语法
    //关键字 : delegate
    //语法:访问修饰符 delegate 返回值 委托名(参数列表);

    //写在哪里?
    //可以申明在namespace和class语句块中
    //更多的写在namespace中

    //简单记忆委托语法 就是 函数申明语法前面加一个delegate关键字
    #endregion

    #region 知识点三 定义自定义委托
    //访问修饰默认不写 为public 在别的命名空间中也能使用
    //private 其它命名空间就不能用了
    //一般使用public

    //申明了一个可以用来存储无参无返回值函数的容器
    //这里只是定义了规则 并没有使用
    delegate void MyFun();
    //委托规则的申明 是不能重名(同一语句块中)
    //表示用来装载或传递 返回值为int 有一个int参数的函数的 委托 容器规则
    public delegate int MyFun2(int a);

    //委托是支持 泛型的 可以让返回值和参数 可变 更方便我们的使用
    delegate T MyFun3<T, K>(T v, K k);
    #endregion

    #region 知识点四 使用定义好的委托
    //委托变量是函数的容器

    //委托常用在:
    //1.作为类的成员
    //2.作为函数的参数
    class Test
    {
        public MyFun fun;
        public MyFun2 fun2;

        public Action action;

        public void TestFun( MyFun fun, MyFun2 fun2 )
        {
            //先处理一些别的逻辑 当这些逻辑处理完了 再执行传入的函数
            int i = 1;
            i *= 2;
            i += 2;

            //fun();
            //fun2(i);
            //this.fun = fun;
            //this.fun2 = fun2;
        }


        #region 增
        public void AddFun(MyFun fun, MyFun2 fun2)
        {
            this.fun += fun;
            this.fun2 += fun2;
        }
        #endregion

        #region 删
        public void RemoveFun(MyFun fun, MyFun2 fun2)
        {
            //this.fun = this.fun - fun;
            this.fun -= fun;
            this.fun2 -= fun2;
        }
        #endregion
    }
    #endregion

    #region 知识点五 委托变量可以存储多个函数(多播委托)


    #endregion

    

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("委托");
            //专门用来装载 函数的 容器
            MyFun f = new MyFun(Fun);
            Console.WriteLine("1");
            Console.WriteLine("2");
            Console.WriteLine("3");
            Console.WriteLine("4");
            Console.WriteLine("5");
            f.Invoke();

            MyFun f2 = Fun;
            Console.WriteLine("1");
            Console.WriteLine("2");
            Console.WriteLine("3");
            Console.WriteLine("4");
            Console.WriteLine("5");
            f2();

            MyFun2 f3 = Fun2;
            Console.WriteLine(f3(1));

            MyFun2 f4 = new MyFun2(Fun2);
            Console.WriteLine(f4.Invoke(3));

            Test t = new Test();

            t.TestFun(Fun, Fun2);
            Console.WriteLine("***************");
            //如何用委托存储多个函数
            MyFun ff = null;
            //ff = ff + Fun;
            ff += Fun;
            ff += Fun3;
            ff();
            //从容器中移除指定的函数
            ff -= Fun;
            //多减 不会报错 无非就是不处理而已
            ff -= Fun;
            ff();
            //清空容器
            ff = null;
            if( ff != null )
            {
                ff();
            }

            #region 知识点六 系统定义好的委托
            //使用系统自带委托 需要引用using System;
            //无参无返回值
            Action action = Fun;
            action += Fun3;
            action();

            //可以指定返回值类型的 泛型委托
            Func<string> funcString = Fun4;
            Func<int> funcInt = Fun5;

            //可以传n个参数的  系统提供了 1到16个参数的委托 直接用就行了
            Action<int, string> action2 = Fun6;

            //可以穿n个参数的 并且有返回值的 系统也提供了 16个委托
            Func<int, int> func2 = Fun2;
            #endregion
        }

        static void Fun()
        {
            Console.WriteLine("张三做什么");
        }
        static void Fun3()
        {
            Console.WriteLine("李四做什么");
        }

        static string Fun4()
        {
            return "";
        }

        static int Fun5()
        {
            return 1;
        }

        static void Fun6(int i, string s)
        {

        }

        static int Fun2(int value)
        {
            return value;
        }
    }

    //总结
    //简单理解 委托 就是装载、传递函数的容器而已
    //可以用委托变量 来存储函数或者传递函数的
    //系统其实已经提供了很多委托给我们用 
    //Action:没有返回值,参数提供了 0~16个委托给我们用
    //Func:有返回值,参数提供了 0~16个委托给我们用
}

练习题

using System;

namespace Lesson12_练习题
{
    #region 练习题一
    //一家三口,妈妈做饭,爸爸妈妈和孩子都要吃饭
    //用委托模拟做饭——>开饭——>吃饭的过程
    abstract class Person
    {
        public abstract void Eat();
    }

    class Mother : Person
    {
        public Action beginEat;

        public override void Eat()
        {
            Console.WriteLine("妈妈吃饭");
        }

        public void DoFood()
        {
            Console.WriteLine("妈妈做饭");

            Console.WriteLine("妈妈做饭做好了");

            //执行委托函数
            if(beginEat != null)
            {
                beginEat();
            }
        }
    }

    class Father:Person
    {
        public override void Eat()
        {
            Console.WriteLine("爸爸吃饭");
        }
    }

    class Son:Person
    {
        public override void Eat()
        {
            Console.WriteLine("孩子吃饭");
        }
    }

    #endregion

    #region 练习题二
    //怪物死亡后,玩家要加10块钱,界面要更新数据
    //成就要累加怪物击杀数,请用委托来模拟实现这些功能
    //只用写核心逻辑表现这个过程,不用写的太复杂

    class Monster
    {
        //当怪物死亡时 把自己作为参数传出去 
        public Action<Monster> deadDoSomthing;
        //怪物成员变量 特征 价值多少钱
        public int money = 10;

        public void Dead()
        {
            Console.WriteLine("怪物死亡");
            if(deadDoSomthing != null)
            {
                deadDoSomthing(this);
            }
            //一般情况下 委托关联的函数 有加 就有减(或者直接清空)
            deadDoSomthing = null;
        }
    }

    class Player
    {
        private int myMoney = 0;

        public void MonsterDeadDoSomthing(Monster m)
        {
            myMoney += m.money;
            Console.WriteLine("现在有{0}元钱", myMoney);
        }
    }

    class Panel
    {
        private int nowShowMoney = 0;

        public void MonsterDeadDo(Monster m)
        {
            nowShowMoney += m.money;
            Console.WriteLine("当前面板显示{0}元钱", nowShowMoney);
        }
    }

    class CJ
    {
        private int nowKillMonsterNum = 0;

        public void MonsterDeadDo(Monster m)
        {
            nowKillMonsterNum += 1;
            Console.WriteLine("当前击杀了{0}怪物", nowKillMonsterNum);
        }
    }
    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("委托练习题");

            Mother m = new Mother();
            Father f = new Father();
            Son s = new Son();
            //告诉妈妈 一会做好了 我要吃
            m.beginEat += f.Eat;
            m.beginEat += s.Eat;
            m.beginEat += m.Eat;
            //做饭
            m.DoFood();

            Monster monster = new Monster();
            Player p = new Player();
            Panel panel = new Panel();
            CJ cj = new CJ();

            monster.deadDoSomthing += p.MonsterDeadDoSomthing;
            monster.deadDoSomthing += panel.MonsterDeadDo;
            monster.deadDoSomthing += cj.MonsterDeadDo;

            monster.Dead();
            monster.Dead();

            Monster monster2 = new Monster();
            monster2.deadDoSomthing += p.MonsterDeadDoSomthing;
            monster2.deadDoSomthing += panel.MonsterDeadDo;
            monster2.deadDoSomthing += cj.MonsterDeadDo;
            monster2.Dead();
        }
    }
}

事件

using System;

namespace Lesson13_事件
{
    #region 知识点一 事件是什么
    //事件是基于委托的存在
    //事件是委托的安全包裹
    //让委托的使用更具有安全性
    //事件 是一种特殊的变量类型
    #endregion

    #region 知识点二 事件的使用
    //申明语法:
    //访问修饰符 event 委托类型 事件名;
    //事件的使用:
    //1.事件是作为 成员变量存在于类中
    //2.委托怎么用 事件就怎么用
    //事件相对于委托的区别:
    //1.不能在类外部 赋值
    //2.不能再类外部 调用
    //注意:
    //它只能作为成员存在于类和接口以及结构体中
    class Test
    {
        //委托成员变量 用于存储 函数的
        public Action myFun;
        //事件成员变量 用于存储 函数的
        public event Action myEvent;

        public Test()
        {
            //事件的使用和委托 一模一样 只是有些 细微的区别
            myFun = TestFun;
            myFun += TestFun;
            myFun -= TestFun;
            myFun();
            myFun.Invoke();
            myFun = null;

            myEvent = TestFun;
            myEvent += TestFun;
            myEvent -= TestFun;
            myEvent();
            myEvent.Invoke();
            myEvent = null;
        }

        public void DoEvent()
        {
            if(myEvent != null)
            {
                myEvent();
            }
        }

        public void TestFun()
        {
            Console.WriteLine("123");
        }
    }
    #endregion

    #region 知识点三 为什么有事件
    //1.防止外部随意置空委托
    //2.防止外部随意调用委托
    //3.事件相当于对委托进行了一次封装 让其更加安全
    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("事件");

            Test t = new Test();
            //委托可以在外部赋值
            t.myFun = null;
            t.myFun = TestFun;
            t.myFun = t.myFun + TestFun;
            t.myFun += TestFun;
            //事件是不能再外部赋值的
            //t.myEvent = null;
            //t.myEvent = TestFun;
            //虽然不能直接赋值 但是可以 加减 去添加移除记录的函数
            t.myEvent += TestFun;
            t.myEvent -= TestFun;

            //委托是可以在外部调用的
            t.myFun();
            t.myFun.Invoke();
            //事件不能再外部调用
            //t.myEvent();
            //只能在类的内部去封装 调用
            t.DoEvent();

            Action a = TestFun;
            //事件 是不能作为临时变量在函数中使用的
            //event Action ae = TestFun;
        }

        static void TestFun()
        {

        }
    }
    //总结
    //事件和委托的区别
    //事件和委托的使用基本是一模一样的
    //事件就是特殊的委托
    //主要区别:
    //1.事件不能再外部使用赋值=符号,只能使用+ - 委托 哪里都能用
    //2.事件 不能再外部执行 委托哪里都能执行
    //3.事件 不能作为 函数中的临时变量的 委托可以
}

匿名函数

using System;

namespace Lesson14_匿名函数
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("匿名函数");

            #region 知识点一 什么是匿名函数
            //顾名思义,就是没有名字的函数
            //匿名函数的使用主要是配合委托和事件进行使用
            //脱离委托和事件 是不会使用匿名函数的
            #endregion

            #region 知识点二 基本语法
            //delegate (参数列表)
            //{
            //    //函数逻辑
            //};
            //何时使用?
            //1.函数中传递委托参数时
            //2.委托或事件赋值时
            #endregion

            #region 知识点三 使用
            //1.无参无返回
            //这样申明匿名函数 只是在申明函数而已 还没有调用
            //真正调用它的时候 是这个委托容器啥时候调用 就什么时候调用这个匿名函数
            Action a = delegate ()
            {
                Console.WriteLine("匿名函数逻辑");
            };

            a();
            //2.有参
            Action<int, string> b = delegate (int a, string b)
            {
                Console.WriteLine(a);
                Console.WriteLine(b);
            };

            b(100, "123");
            //3.有返回值
            Func<string> c = delegate ()
            {
                return "123123";
            };

            Console.WriteLine(c());

            //4.一般情况会作为函数参数传递 或者 作为函数返回值
            Test t = new Test();
            Action ac = delegate ()
            {
                Console.WriteLine("随参数传入的匿名函数逻辑");
            };
            t.Dosomthing(50, ac);
            //  参数传递
            t.Dosomthing(100, delegate ()
            {
                Console.WriteLine("随参数传入的匿名函数逻辑");
            });

            //  返回值
            Action ac2 = t.GetFun();
            ac2();
            //一步到位 直接调用返回的 委托函数
            t.GetFun()();
            #endregion

            #region 知识点四 匿名函数的缺点
            //添加到委托或事件容器中后 不记录 无法单独移除

            Action ac3 = delegate ()
            {
                Console.WriteLine("匿名函数一");
            };

            ac3 += delegate ()
            {
                Console.WriteLine("匿名函数二");
            };

            ac3();
            //因为匿名函数没有名字 所以没有办法指定移除某一个匿名函数
            //此匿名函数 非彼匿名函数 不能通过看逻辑是否一样 就证明是一个 
            //ac3 -= delegate ()
            //{
            //    Console.WriteLine("匿名函数一");
            //};
            ac3 = null;
            //ac3();
            #endregion
        }

        static void TestFun()
        {

        }
    }

    class Test
    {
        public Action action;

        //作为参数传递时
        public void Dosomthing(int a, Action fun)
        {
            Console.WriteLine(a);
            fun();
        }

        //作为返回值
        public Action GetFun()
        {
            return delegate() {
                Console.WriteLine("函数内部返回的一个匿名函数逻辑");
            };
        }

        public void TestTTTT()
        {

        }
    }

    //总结
    //匿名函数 就是没有名字的函数
    //固定写法 
    //delegate(参数列表){}
    //主要是在 委托传递和存储时  为了方便可以直接使用倪敏该函数
    //缺点是 没有办法指定移除
}

Lambad表达式

using System;

namespace Lesson15_lambad表达式
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("lambad表达式");

            #region 知识点一 什么是lambad表达式
            //可以将lambad表达式 理解为匿名函数的简写
            //它除了写法不同外
            //使用上和匿名函数一模一样
            //都是和委托或者事件 配合使用的
            #endregion

            #region 知识点二 lambad表达式语法
            //匿名函数
            //delegate (参数列表)
            //{

            //};

            //lambad表达式
            //(参数列表) =>
            //{
            //    //函数体
            //};
            #endregion

            #region 知识点三 使用
            //1.无参无返回
            Action a = () =>
            {
                Console.WriteLine("无参无返回值的lambad表达式");
            };
            a();
            //2.有参
            Action<int> a2 = (int value) =>
            {
                Console.WriteLine("有参数Lambad表达式{0}", value);
            };
            a2(100);

            //3.甚至参数类型都可以省略 参数类型和委托或事件容器一致
            Action<int> a3 = (value) =>
            {
                Console.WriteLine("省略参数类型的写法{0}", value);
            };
            a3(200);
            //4.有返回值
            Func<string, int> a4 = (value) =>
            {
                Console.WriteLine("有返回值有参数的那么大表达式{0}", value);
                return 1;
            };
            Console.WriteLine(a4("123123"));

            //其它传参使用等和匿名函数一样
            //缺点也是和匿名函数一样的
            #endregion

            Test t = new Test();

            t.DoSomthing();
        }
    }
    #region 知识点四 闭包
    //内层的函数可以引用包含在它外层的函数的变量
    //即使外层函数的执行已经终止
    //注意:
    //该变量提供的值并非变量创建时的值,而是在父函数范围内的最终值。

    class Test
    {
        public event Action action;

        public Test()
        {
            int value = 10;
            //这里就形成了闭包
            //因为 当构造函数执行完毕时  其中申明的临时变量value的声明周期被改变了
            action = () =>
            {
                Console.WriteLine(value);
            };

            for (int i = 0; i < 10; i++)
            {
                //此index 非彼index
                int index = i;
                action += () =>
                {
                    Console.WriteLine(index);
                };
            }
        }

        public void DoSomthing()
        {
            action();
        }
    }

    #endregion
    
    //总结
    //匿名函数的特殊写法 就是 lambad表达式
    //固定写法 就是 (参数列表)=>{}
    //参数列表 可以直接省略参数类型
    //主要在 委托传递和存储时  为了方便可以直接使用匿名函数或者lambad表达式

    //缺点:无法指定移除
}

List排序

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Lesson16_List排序
{
    class Item : IComparable<Item>
    {
        public int money;

        public Item(int money)
        {
            this.money = money;
        }

        public int CompareTo(Item other)
        {
            //返回值的含义
            //小于0:
            //放在传入对象的前面
            //等于0:
            //保持当前的位置不变
            //大于0:
            //放在传入对象的后面

            //可以简单理解 传入对象的位置 就是0
            //如果你的返回为负数 就放在它的左边 也就前面
            //如果你返回正数 就放在它的右边 也就是后面

            if( this.money > other.money )
            {
                return -1;
            }
            else
            {
                return 1;
            }
        }
    }

    class ShopItem
    {
        public int id;

        public ShopItem(int id)
        {
            this.id = id;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("List排序");
            #region 知识点一 List自带排序方法
            List<int> list = new List<int>();
            list.Add(3);
            list.Add(2);
            list.Add(6);
            list.Add(1);
            list.Add(4);
            list.Add(5);
            for (int i = 0; i < list.Count; i++)
            {
                Console.WriteLine(list[i]);
            }
            //list提供了排序方法
            list.Sort();
            Console.WriteLine("**************");
            for (int i = 0; i < list.Count; i++)
            {
                Console.WriteLine(list[i]);
            }
            //ArrayList中也有Sort排序方法
            #endregion

            #region 知识点二 自定义类的排序
            List<Item> itemList = new List<Item>();
            itemList.Add(new Item(45));
            itemList.Add(new Item(10));
            itemList.Add(new Item(99));
            itemList.Add(new Item(24));
            itemList.Add(new Item(100));
            itemList.Add(new Item(12));
            //排序方法
            itemList.Sort();
            for (int i = 0; i < itemList.Count; i++)
            {
                Console.WriteLine(itemList[i].money);
            }
            #endregion

            #region 知识点三 通过委托函数进行排序
            List<ShopItem> shopItems = new List<ShopItem>();
            shopItems.Add(new ShopItem(2));
            shopItems.Add(new ShopItem(1));
            shopItems.Add(new ShopItem(4));
            shopItems.Add(new ShopItem(3));
            shopItems.Add(new ShopItem(6));
            shopItems.Add(new ShopItem(5));

            //shopItems.Sort(SortShopItem);
            //匿名函数
            //shopItems.Sort(delegate (ShopItem a, ShopItem b)
            //{
            //    if (a.id > b.id)
            //    {
            //        return -1;
            //    }
            //    else
            //    {
            //        return 1;
            //    }
            //});
            //lambad表达式 配合 三目运算符的 完美呈现
            shopItems.Sort((a, b) =>{ return a.id > b.id ? 1 : -1;});

            Console.WriteLine("*********************");
            for (int i = 0; i < shopItems.Count; i++)
            {
                Console.WriteLine(shopItems[i].id);
            }
            #endregion
        }

        static int SortShopItem( ShopItem a, ShopItem b )
        {
            //传入的两个对象 为列表中的两个对象
            //进行两两的比较  用左边的和右边的条件 比较
            //返回值规则 和之前一样 0做标准 负数在左(前) 正数在右(后)
            if (a.id > b.id)
            {
                return -1;
            }
            else
            {
                return 1;
            }
        }
    }

    //总结
    //系统自带的变量(int,float,double.....) 一般都可以直接Sort
    //自定义类SOrt有两种方式
    //1.继承接口 IComparable
    //2.在Sort中传入委托函数

}

协变逆变

using System;

namespace Lesson17_协变逆变
{
    #region 知识点一 什么是协变逆变
    //协变:
    //和谐的变化,自然的变化
    //因为 里氏替换原则 父类可以装子类
    //所以 子类变父类  
    //比如 string 变成 object 
    //感受是和谐的

    //逆变:
    //逆常规的变化,不正常的变化
    //因为 里氏替换原则 父类可以装子类 但是子类不能装父类
    //所以 父类变子类  
    //比如 object 变成 string
    //感受是不和谐的

    //协变和逆变是用来修饰泛型的
    //协变:out 
    //逆变:in
    //用于在泛型中 修饰 泛型字母的 
    //只有泛型接口和泛型委托能使用
    #endregion

    #region 知识点二 作用
    //1.返回值 和 参数
    //用out修饰的泛型 只能作为返回值
    delegate T TestOut<out T>();
    //用in修饰的泛型 只能作为参数
    delegate void TestIn<in T>(T t);

    //2.结合里氏替换原则理解
    class Father
    {

    }

    class Son:Father
    {

    }
    #endregion
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("协变逆变");
            #region 知识点二 作用(结合里氏替换原则理解)
            //协变 父类总是能被子类替换
            // 看起来 就是 son ——> father
            TestOut<Son> os = () =>
            {
                return new Son();
            };

            TestOut<Father> of = os;
            Father f = of();//实际上 返回的 是os里面装的函数 返回的是Son

            //逆变 父类总是能被子类替换
            //看起来像是 father——>son 明明是传父类 但是你传子类 不和谐的
            TestIn<Father> iF = (value) =>
            {

            };

            TestIn<Son> iS = iF;

            iS(new Son());//实际上 调用的是 iF
            #endregion
        }
    }

    //总结
    //协变 out
    //逆变 in
    //用来修饰 泛型替代符的  只能修饰接口和委托中的泛型

    //作用两点
    //1.out修饰的泛型类型 只能作为返回值类型 in修饰的泛型类型 只能作为 参数类型
    //2.遵循里氏替换原则的  用out和in修饰的 泛型委托 可以相互装载(有父子关系的泛型)
    //  协变  父类泛型委托装子类泛型委托    逆变 子类泛型委托装父类泛型委托
}

多线程

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("多线程");

            #region 知识点一 了解线程前先了解进程
            //进程(Process)是计算机中的程序关于某数据集合上的一次运行活动
            //是系统进行资源分配和调度的基本单位,是操作系统结构的基础
            //说人话:打开一个应用程序就是在操作系统上开启了一个进程
            //进程之间可以相互独立运行,互不干扰
            //进程之间也可以相互访问、操作
            #endregion

            #region 知识点二 什么是线程
            //操作系统能够进行运算调度的最小单位。
            //它被包含在进程之中,是进程中的实际运作单位
            //一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程
            //我们目前写的程序 都在主线程中

            //简单理解线程:
            //就是代码从上到下运行的一条“管道”
            #endregion

            #region 知识点三 什么是多线程
            //我们可以通过代码 开启新的线程
            //可以同时运行代码的多条“管道” 就叫多线程
            #endregion

            #region 知识点四 语法相关
            //线程类 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);
            #endregion

            #region 知识点五 线程之间共享数据
            //多个线程使用的内存是共享的,都属于该应用程序(进程)
            //所以要注意 当多线程 同时操作同一片内存区域时可能会出问题
            //可以通过加锁的形式避免问题

            //lock
            //当我们在多个线程当中想要访问同样的东西 进行逻辑处理时
            //为了避免不必要的逻辑顺序执行的差错
            //lock(引用类型对象)

            while(true)
            {
                lock(obj)
                {
                    Console.SetCursorPosition(0, 0);
                    Console.ForegroundColor = ConsoleColor.Red;
                    Console.Write("●");
                }
            }
            #endregion

            #region 知识点六 多线程对于我们的意义
            //可以用多线程专门处理一些复杂耗时的逻辑
            //比如 寻路、网络通信等等
            #endregion
        }

        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

using System;

namespace Lesson19_预处理器指令
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("预处理器指令");

            #region 知识点一 什么是编译器
            //编译器是一种翻译程序
            //它用于将源语言程序翻译为目标语言程序

            //源语言程序:某种程序设计语言写成的,比如C#、C、C++、Java等语言写的程序
            //目标语言程序:二进制数表示的伪机器代码写的程序
            #endregion

            #region 知识点二 什么是预处理器指令
            //预处理器指令 指导编译器 在实际编译开始之前对信息进行预处理
            //预处理器指令 都是以#开始
            //预处理器指令不是语句,所以它们不以分号;结束
            //目前我们经常用到的 折叠代码块 就是预处理器指令
            #endregion

            #region 知识点三 常见的预处理器指令
            //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使用
            #endregion
        }
    }

    //总结
    //预处理器指令
    //可以让代码还没有编译之前就可以进行一些预处理判断
    //在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.程序运行时创建新对象,用这些对象执行任务
    #endregion

    class 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引起的基本工作机制 就是建立在反射的基础上


}

练习题

using System;
using System.Reflection;

namespace Lesson20_练习题
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("反射练习题");
            #region 练习题
            //新建一个类库工程
            //有一个Player类,有姓名,血量,攻击力,防御力,位置等信息
            //有一个无参构造函数
            //再新建一个控制台工程
            //通过反射的形式使用类库工程生成的dll程序集
            //实例化一个Player对象
            //加载类库生成的 程序集 dll库文件
            Assembly assembly = Assembly.LoadFrom(@"C:\Users\MECHREVO\Desktop\CSharp进阶教学\测试\bin\Debug\测试");
            Type[] types = assembly.GetTypes();
            for (int i = 0; i < types.Length; i++)
            {
                Console.WriteLine(types[i]);
            }

            Type player = assembly.GetType("MrTang.Player");
            
            object obj = Activator.CreateInstance(player);
            Console.WriteLine(obj);
            #endregion
        }
    }
}

特性

#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引擎中很多地方都用到了特性来进行一些特殊处理
}

习题

using System;
using System.Reflection;

namespace Lesson21_练习题
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("特性练习题");

            #region 练习题
            //为反射练习题中的Player对象
            //随便为其中一个成员变量加一个自定义特性
            //同样实现反射练习题中的要求

            //但是当在设置加了自定义特性的成员变量时,在控制台中打印一句
            //非法操作,随意修改XXX成员

            Assembly assembly = Assembly.LoadFrom(@"C:\Users\MECHREVO\Desktop\CSharp进阶教学\测试\bin\Debug\测试");
            Type[] types = assembly.GetTypes();
            for (int i = 0; i < types.Length; i++)
            {
                Console.WriteLine(types[i]);
            }
            //得type
            Type playerType = assembly.GetType("MrTang.Player");
            //实例化
            object playerObj = Activator.CreateInstance(playerType);
            Console.WriteLine(playerObj);

            FieldInfo[] fields = playerType.GetFields();
            for (int i = 0; i < fields.Length; i++)
            {
                Console.WriteLine(fields[i]);
            }

            //首先要得到我们自定特性的Type
            Type attribute = assembly.GetType("MrTang.MyCustomAttribute");

            //赋值名字
            FieldInfo fildStr = playerType.GetField("name");
            //得到的特性如果不为空 就证明有
            if(fildStr.GetCustomAttribute(attribute) != null)
            {
                Console.WriteLine("非法操作,随意修改name成员");
            }
            else
            {
                //检测是否被自定义特性修饰 如果是 就不能修改 而是提示
                fildStr.SetValue(playerObj, "123123");
            }
           

            #endregion
        }
    }
}

迭代器

using System;
using System.Collections;

namespace Lesson22_迭代器
{
    #region 知识点一 迭代器是什么
    //迭代器(iterator)有时又称光标(cursor)
    //是程序设计的软件设计模式
    //迭代器模式提供一个方法顺序访问一个聚合对象中的各个元素
    //而又不暴露其内部的标识

    //在表现效果上看
    //是可以在容器对象(例如链表或数组)上遍历访问的接口
    //设计人员无需关心容器对象的内存分配的实现细节
    //可以用foreach遍历的类,都是实现了迭代器的
    #endregion

    #region 知识点二 标准迭代器的实现方法
    //关键接口:IEnumerator,IEnumerable
    //命名空间:using System.Collections;
    //可以通过同时继承IEnumerable和IEnumerator实现其中的方法

    class CustomList : IEnumerable, IEnumerator
    {
        private int[] list;
        //从-1开始的光标 用于表示 数据得到了哪个位置
        private int position = -1;

        public CustomList()
        {
            list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
        }

        #region IEnumerable
        public IEnumerator GetEnumerator()
        {
            Reset();
            return this;
        }
        #endregion

        public object Current
        {
            get
            {
                return list[position];
            }
        }
        public bool MoveNext()
        {
            //移动光标
            ++position;
            //是否溢出 溢出就不合法
            return position < list.Length;
        }

        //reset是重置光标位置 一般写在获取 IEnumerator对象这个函数中
        //用于第一次重置光标位置
        public void Reset()
        {
            position = -1;
        }
    }
    #endregion

    #region 知识点三 用yield return 语法糖实现迭代器
    //yield return 是C#提供给我们的语法糖
    //所谓语法糖,也称糖衣语法
    //主要作用就是将复杂逻辑简单化,可以增加程序的可读性
    //从而减少程序代码出错的机会

    //关键接口:IEnumerable
    //命名空间:using System.Collections;
    //让想要通过foreach遍历的自定义类实现接口中的方法GetEnumerator即可

    class CustomList2 : IEnumerable
    {
        private int[] list;

        public CustomList2()
        {
            list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8 };
        }

        public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < list.Length; i++)
            {
                //yield关键字 配合迭代器使用
                //可以理解为 暂时返回 保留当前的状态
                //一会还会在回来
                //C#的语法糖
                yield return list[i];
            }
            //yield return list[0];
            //yield return list[1];
            //yield return list[2];
            //yield return list[3];
            //yield return list[4];
            //yield return list[5];
            //yield return list[6];
            //yield return list[7];
        }
    }

    #endregion

    #region 知识点四 用yield return 语法糖为泛型类实现迭代器
    class CustomList<T> : IEnumerable
    {
        private T[] array;

        public CustomList(params T[] array)
        {
            this.array = array;
        }

        public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < array.Length; i++)
            {
                yield return array[i];
            }
        }
    }
    #endregion

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("迭代器");

            CustomList list = new CustomList();

            //foreach本质 
            //1.先获取in后面这个对象的 IEnumerator
            //  会调用对象其中的GetEnumerator方法 来获取
            //2.执行得到这个IEnumerator对象中的 MoveNext方法
            //3.只要MoveNext方法的返回值时true 就会去得到Current
            //  然后复制给 item
            //foreach (int item in list)
            //{
            //    Console.WriteLine(item);
            //}

            //foreach (int item in list)
            //{
            //    Console.WriteLine(item);
            //}

            CustomList<string> list2 = new CustomList<string>("123","321","333","555");
            foreach (string item in list2)
            {
                Console.WriteLine(item);
            }
            foreach (string item in list2)
            {
                Console.WriteLine(item);
            }

        }
    }
}

//总结:
//迭代器就是可以让我们在外部直接通过foreach遍历对象中元素而不需要了解其结构
//主要的两种方式
//1.传统方式 继承两个接口 实现里面的方法
//2.用语法糖 yield return 去返回内容 只需要继承一个接口即可

习题

using System;
using System.Collections;

namespace Lesson22_练习题
{
    #region 练习题
    //请为一个自定义类
    //用两种方法让其可以被foreach遍历

    class CustomList : IEnumerable
    {
        private int[] list;

        public CustomList()
        {
            list = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
        }

        public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < list.Length; i++)
            {
                yield return list[i];
            }
        }
    }

    class CustomList2 : IEnumerable, IEnumerator
    {
        private string[] list;

        private int position = -1;

        public CustomList2()
        {
            list = new string[] { "123", "321", "666", "7777" };
        }

        public object Current
        {
            get
            {
                return list[position];
            }
        }

        public IEnumerator GetEnumerator()
        {
            Reset();
            return this;
        }

        public bool MoveNext()
        {
            ++position;
            return position < list.Length;
        }

        public void Reset()
        {
            position = -1;
        }
    }

    #endregion
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("迭代器练习题");
z
            CustomList list = new CustomList();
            foreach (int item in list)
            {
                Console.WriteLine(item);
            }

            CustomList2 list2 = new CustomList2();
            foreach (string item in list2)
            {
                Console.WriteLine(item);
            }
            foreach (string item in list2)
            {
                Console.WriteLine(item);
            }
        }
    }
}

特殊语法

using System;
using System.Collections.Generic;

namespace Lesson23_特殊语法
{
    class Person
    {
        private int money;
        public bool sex;

        public string Name
        {
            get => "唐老狮";
            set => sex = true;
        }

        public int Age
        {
            get;
            set;
        }

        public Person(int money)
        {
            this.money = money;
        }

        public int Add(int x, int y) => x + y;

        public void Speak(string str) => Console.WriteLine(str);
    }

    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("特殊语法");

            #region 知识点一 var隐式类型
            //var是一种特殊的变量类型
            //它可以用来表示任意类型的变量
            //注意:
            //1.var不能作为类的成员 只能用于临时变量申明时
            //  也就是 一般写在函数语句块中
            //2.var必须初始化
            var i = 5;
            var s = "123";
            var array = new int[] { 1, 2, 3, 4 };
            var list = new List<int>();
            #endregion

            #region 知识点二 设置对象初始值
            //申明对象时 
            //可以通过直接写大括号的形式初始化公共成员变量和属性
            Person p = new Person(100) { sex = true, Age = 18, Name = "唐老狮" };
            Person p2 = new Person(200) { Age = 18 };
            #endregion

            #region 知识点三 设置集合初始值
            //申明集合对象时
            //也可以通过大括号 直接初始化内部属性
            int[] array2 = new int[] { 1, 2, 3, 4, 5 };

            List<int> listInt = new List<int>() { 1, 2, 3, 4, 5, 6 };

            List<Person> listPerson = new List<Person>() {
                new Person(200),
                new Person(100){Age = 10},
                new Person(1){sex = true, Name = "唐老狮"}
            };

            Dictionary<int, string> dic = new Dictionary<int, string>()
            {
                { 1, "123" },
                { 2, "222"}
            };

            #endregion

            #region 知识点四 匿名类型
            //var 变量可以申明为自定义的匿名类型
            var v = new { age = 10, money = 11, name = "小明" };
            Console.WriteLine(v.age);
            Console.WriteLine(v.name);
            #endregion

            #region 知识点五 可空类型
            //1.值类型是不能赋值为 空的
            //int c = null;
            //2.申明时 在值类型后面加? 可以赋值为空
            int? c = 3;
            //3.判断是否为空
            if( c.HasValue )
            {
                Console.WriteLine(c);
                Console.WriteLine(c.Value);
            }
            //4.安全获取可空类型值
            int? value = null;
            //  4-1.如果为空 默认返回值类型的默认值
            Console.WriteLine(value.GetValueOrDefault());
            //  4-2.也可以指定一个默认值
            Console.WriteLine(value.GetValueOrDefault(100));

            float? f = null;
            double? d = null;

            
            object o = null;
            if( o != null )
            {
                Console.WriteLine(o.ToString());
            }
            //相当于是一种语法糖 能够帮助我们自动去判断o是否为空
            //如果是null就不会执行tostring也不会报错
            Console.WriteLine(o?.ToString());

            int[] arrryInt = null;

            Console.WriteLine(arrryInt?[0]);

            Action action = null;
            //if (action != null)
            //{
            //    action();
            //}
            action?.Invoke();

            #endregion

            #region 知识点六 空合并操作符
            // 空合并操作符 ??
            // 左边值 ?? 右边值
            // 如果左边值为null 就返回右边值 否则返回左边值
            // 只要是可以为null的类型都能用

            int? intV = null;
            //int intI = intV == null ? 100 : intV.Value;
            int intI = intV ?? 100;
            Console.WriteLine(intI);

            string str = null;
            str = str ?? "hahah";
            Console.WriteLine(str);
            #endregion

            #region 知识点七 内插字符串
            //关键符号:$
            //用$来构造字符串,让字符串中可以拼接变量
            string name = "唐老狮";
            int age = 18;
            Console.WriteLine($"好好学习,{name},年龄:{age}");
            #endregion

            #region 知识点八 单句逻辑简略写法
            //当循环或者if语句中只有 一句代码时 大括号可以省略
            if (true)
                Console.WriteLine("123123");

            for (int j = 0; j < 10; j++)
                Console.WriteLine(j);

            while (true)
                Console.WriteLine("123123");

            #endregion
        }
    }
}

值和引用

using System;
using System.Collections.Generic;

namespace Lesson24_值和引用
{
    class Test
    {
        public static int TestI = 0;

        int b = 0;

        string str = "123";

        TestStrict ts = new TestStrict();
        public void Fun()
        {
            b = 1;
        }
    }

    struct TestStrict
    {
        public Test t;
        public int i;
    }

    class Program
    {
        static int b;
        static void Main(string[] args)
        {
            Console.WriteLine("值和引用");
            #region 知识回顾
            //值类型
            //无符号:byte,ushort,uint,ulong
            //有符号:sbyte,short,int,long
            //浮点数:float,double,decimal
            //特殊:char,bool
            //枚举:enum
            //结构体:struct

            //引用类型
            //string
            //数组
            //class
            //interface
            //委托

            //值类型和引用类型的本质区别
            //值的具体内容存在栈内存上
            //引用的具体内容存在堆内存上
            #endregion

            #region 问题一 如何判断 值类型和引用类型
            //F12进到类型的内部去查看
            //是class就是引用
            //是struct就是值
            int i = 12;
            string str = "123";
            #endregion

            #region 问题二 语句块
            //命名空间
            //   ↓
            //类、接口、结构体
            //   ↓
            //函数、属性、索引器、运算符重载等(类、接口、结构体)
            //   ↓
            //条件分支、循环

            //上层语句块:类、结构体
            //中层语句块:函数
            //底层的语句块: 条件分支 循环等

            //我们的逻辑代码写在哪里?
            //函数、条件分支、循环-中底层语句块中

            //我们的变量可以申明在哪里?
            //上、中、底都能申明变量
            //上层语句块中:成员变量
            //中、底层语句块中:临时变量
            #endregion

            #region 问题三 变量的生命周期
            //编程时大部分都是 临时变量
            //在中底层申明的临时变量(函数、条件分支、循环语句块等)
            //语句块执行结束 
            //没有被记录的对象将被回收或变成垃圾
            //值类型:被系统自动回收
            //引用类型:栈上用于存地址的房间被系统自动回收,堆中具体内容变成垃圾,待下次GC回收

            int i2 = 1;
            string str2 = "123";

            //{
            //    int b = 1;
            //}
            //Console.WriteLine(b);
            //while(true)
            //{
            //    int index = 1;
            //}

            //想要不被回收或者不变垃圾
            //必须将其记录下来
            //如何记录?
            //在更高层级记录或者
            //使用静态全局变量记录
            b = 0;
            if(true)
            {
                b = 1;
            }

            int c = 10;
            Test.TestI = c;

            //Game g = new Game();
            //while(true)
            //{

            //}
            #endregion

            #region 问题四 结构体中的值和引用
            //结构体本身是值类型
            //前提:该结构体没有做为其它类的成员
            //在结构体中的值,栈中存储值具体的内容
            //在结构体中的引用,堆中存储引用具体的内容

            //引用类型始终存储在堆中
            //真正通过结构体使用其中引用类型时只是顺藤摸瓜

            TestStrict ts = new TestStrict();
            #endregion

            #region 问题五 类中的值和引用
            //类本身是引用类型
            //在类中的值,堆中存储具体的值
            //在类中的引用,堆中存储具体的值

            //值类型跟着大哥走,引用类型一根筋
            Test t = new Test();
            #endregion

            #region 问题六 数组中的存储规则
            //数组本身是引用类型
            //值类型数组,堆中房间存具体内容
            //引用类型数组,堆中房间存地址
            int[] arrayInt = new int[5];
            object[] objs = new object[5];
            #endregion

            #region 问题七 结构体继承接口
            //利用里氏替换原则,用接口容器装载结构体存在装箱拆箱

            TestStruct obj1 = new TestStruct();
            obj1.Value = 1;
            Console.WriteLine(obj1.Value);
            TestStruct obj2 = obj1;
            obj2.Value = 2;
            Console.WriteLine(obj1.Value);
            Console.WriteLine(obj2.Value);

            ITest iObj1 = obj1;//装箱  value 1
            ITest iObj2 = iObj1;
            iObj2.Value = 99;
            Console.WriteLine(iObj1.Value);
            Console.WriteLine(iObj2.Value);

            TestStruct obj3 = (TestStruct)iObj1;//拆箱

            #endregion
        }
    }

    interface ITest
    {
        int Value
        {
            get;
            set;
        }
    }

    struct TestStruct : ITest
    {
        int value;
        public int Value 
        {
            get
            {
                return value;
            }
            set
            {
                this.value = value;
            }
        
        }
    }

}

俄罗斯方块实战

需求分析

开始场景结束场景复用贪吃蛇

image-20250118071917547

image-20250118235446850

image-20250118235634940

image-20250118072224358

项目文件

image-20250118235703992

BeginOrEndScene

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using 俄罗斯方块;
namespace 俄罗斯方块
{
   abstract class BeginOrEndScene : IsceneUpdate
    {
        protected int nowSelIndex = 0;
        protected string strTitle;
        protected string StrOne;
        public abstract void EnterJDoSomthing();
        public void Update()
        {
            // 开始或结束游戏逻辑
            //选择当前选项,监听键盘输入

            Console.ForegroundColor = ConsoleColor.White;
            // 显示标题
            Console.SetCursorPosition(Game.w/2-strTitle.Length,5);
            Console.Write(strTitle);
            Console.SetCursorPosition(Game.w / 2 - StrOne.Length, 8);
            Console.ForegroundColor = nowSelIndex==0?ConsoleColor.Red:ConsoleColor.White;
            Console.Write(StrOne);
            Console.SetCursorPosition(Game.w / 2 -4, 10);
            Console.ForegroundColor = nowSelIndex == 1 ? ConsoleColor.Red : ConsoleColor.White;
            Console.Write("结束游戏");
            switch (Console.ReadKey(true).Key)
            {
                case ConsoleKey.W:
                    --nowSelIndex;
                    if (nowSelIndex <0)
                    {
                        nowSelIndex = 0;
                    }
                    break;
                case ConsoleKey.A:
                    ++nowSelIndex;
                    if (nowSelIndex >1)
                    {
                        nowSelIndex = 1;
                    }
                    break;
                case ConsoleKey.S:
                    ++nowSelIndex;
                    if (nowSelIndex > 1)
                    {
                        nowSelIndex = 1;
                    }
                    break;
                case ConsoleKey.D:
                    --nowSelIndex;
                    if (nowSelIndex < 0)
                    {
                        nowSelIndex = 0;
                    }
                    break;
                case ConsoleKey.J:
                    EnterJDoSomthing();
                    break;


            }

            // 显示下方选项


            // 检测输入


        }

        
    }
}

BeginScene

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace 俄罗斯方块
{
    internal class BeginScene : BeginOrEndScene

    {
        public BeginScene() {
            strTitle = "俄罗斯方块";
            StrOne = "开始游戏";
        }

        public override void EnterJDoSomthing()
        {
            // 按J键做什么
            if (nowSelIndex == 0)
            {
                Game.ChangeScene(E_SceneType.Game);
            }
            else {
                Environment.Exit(0);
            }
        }
    }
}


EndScene

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 俄罗斯方块
{
    internal class EndScene : BeginOrEndScene
    {
        public EndScene() {
            strTitle = "结束游戏";
            StrOne = "回到开始界面";
                
        }
        public override void EnterJDoSomthing()
        {
            // 按J键做什么
            if (nowSelIndex == 0)
            {
                Game.ChangeScene(E_SceneType.Begin);
            }
            else
            {
                Environment.Exit(0);
            }
        }
    }
}


Games

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 俄罗斯方块
{
    enum E_SceneType
    { 
        /// <summary>
        /// 开始
        /// </summary>
        Begin,
        /// <summary>
        /// 游戏
        /// </summary>
        Game,
        /// <summary>
        /// 结束
        /// </summary>
        End
    
    }
    class Game
    {
        public const int w = 50;
        public const int h = 35;
        public static IsceneUpdate nowSecene;

        public Game()
        {

            Console.CursorVisible = false;
            Console.SetWindowSize(w, h);
            Console.SetBufferSize(w, h);
            ChangeScene(E_SceneType.Begin);

        
        }
        public void Start() {
            while (true)
            {
                if (nowSecene != null)
                {
                    nowSecene.Update();
                }

            }
        }
        public static void ChangeScene(E_SceneType type)
        {
            // 切场景之前把上一个场景绘制清理
            Console.Clear();
            switch (type)
            {
                case E_SceneType.Begin:
                    nowSecene = new BeginScene();

                    break;
                case E_SceneType.Game:
                    nowSecene = new GameScene();
                    break;
                case E_SceneType.End:
                    nowSecene = new EndScene();
                    break;
            }
        }

    }
    
}



GameScene

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 俄罗斯方块
{

    class GameScene : IsceneUpdate

    {

        Map map;
        BlockWorker blockWorker;
        Score Score;



        //Thread inputThread;
        //public bool isRunning = true;
        public GameScene()
        {

            map = new Map(this);
            blockWorker = new BlockWorker();
            Score = new Score(this);
            // 添加输入时间监听
            inputThread.Instance.inputEvent += CheckInputThread;

            //inputThread = new Thread(CheckInputThread);
            //inputThread.IsBackground = true;
            //inputThread.Start();


        }
        private void CheckInputThread()
        {
            //while (isRunning)
            //{
            if (Console.KeyAvailable)
            {
                lock (blockWorker)
                {
                    switch (Console.ReadKey(true).Key)
                    {
                        case ConsoleKey.LeftArrow:
                            if (blockWorker.IsChange(E_Change_Type.left, map))
                            {
                                blockWorker.Change(E_Change_Type.left);
                            }
                            break;

                        case ConsoleKey.RightArrow:
                            if (blockWorker.IsChange(E_Change_Type.right, map))
                            {
                                blockWorker.Change(E_Change_Type.right);

                            }
                            break;
                        case ConsoleKey.A:
                            if (blockWorker.IsMoveRL(E_Change_Type.left, map))
                            {
                                blockWorker.MoveRl(E_Change_Type.left);

                            }
                            break;
                        case ConsoleKey.D:
                            if (blockWorker.IsMoveRL(E_Change_Type.right, map))
                            {
                                blockWorker.MoveRl(E_Change_Type.right);

                            }
                            break;
                        case ConsoleKey.S:
                            if (blockWorker.IsAutoMove(map))
                            {
                                blockWorker.AutoMoveDown();
                            }
                            break;


                    }

                    //Score.ScoreLine = map.updateScore();
                    //Score.ScoreNum =  map.GetScore();
                    
                }
            }
        }
        public void StopThread()
        {
            // 移除事件监听
            inputThread.Instance.inputEvent-=CheckInputThread;
        }
        
        public void Update()
        {
            lock (blockWorker)
            {

                /*   Console.ReadKey(true);
                   Game.ChangeScene(E_SceneType.End);*/
                map.IDraw();
                blockWorker.IDraw();
                if (blockWorker.IsAutoMove(map))
                {
                    blockWorker.AutoMoveDown();
                }
                //Score.DrawScore(0, 0);
                if (map.cleared)
                {
                    Score.DrawScore(map.GetScore(),map.updateScore());
                    map.cleared = false;
                }
               
            }
            // 线程休眠

            Thread.Sleep(100);
        }

        // 
    }
}

    

IsceneUpdate.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 俄罗斯方块
{
    // 场景更新接口
    internal interface IsceneUpdate
    {
        void Update();

    }
}

Map

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 俄罗斯方块
{
    internal class Map:IDraw
    {
        // 固定墙壁
        private List<DrawObject> walls = new List<DrawObject>();
        // 动态墙壁
        public List<DrawObject> dynamicWalls = new List<DrawObject>();

        private GameScene nowGameScene;
        // 记录垮掉的行次数
       public int ScoreCount;
        public int Score;
        
        // 快速得到地图边界
        public int w;
        public int h;
        // 跨层
        //记录有多少方块
        private int[] recordInfo; 

        // 重载无参构造初始化固定墙壁
        public Map(GameScene scene) {
            this.nowGameScene = scene;
            h= Game.h-6;
            // 代表每行计数的初始化,默认都为0
            recordInfo = new int[h];
            // 动态墙壁的宽容量
            w = 0;
            // 横向

            for (int i = 0; i < Game.w; i+=2)
            {
                walls.Add(new DrawObject(E_DrawType.wall, i, Game.h - 6));
                walls.Add(new DrawObject(E_DrawType.wall, i, Game.h-1));
                ++w;
            }
            w -= 2;
            // 纵向
            for (int i = 0; i < Game.h; i += 1)
            {
                walls.Add(new DrawObject(E_DrawType.wall, 0, i));
                walls.Add(new DrawObject(E_DrawType.wall, Game.w-2,i));

            }
        }

      

        public void IDraw()
        {
            // 绘制固定墙壁
            for (int i = 0; i < walls.Count; i++)
            {
                walls[i].Draw();

            }
            // 绘制动态墙壁
            for (int i = 0; i < dynamicWalls.Count; i++)
            {
                dynamicWalls[i].Draw();

            }
           

        }
        // 清除动态墙壁
        public void ClearDraw() {
            for (int i = 0; i < dynamicWalls.Count; i++)
            {
                dynamicWalls[i].ClearDraw();

            }

        }

        /// <summary>
        /// 提供给外部动态添加方块的函数
        /// </summary>
        /// <param name="walls"></param>
        public void AddWall(List<DrawObject> walls)
        {
            for (int i = 0; i < walls.Count; i++)
            {

                walls[i].ChangType(E_DrawType.wall);
                dynamicWalls.Add(walls[i]);
                // 在动态墙壁添加出发现位置满了就结束
                if (walls[i].Position.y <= 0)
                {
                    // 关闭输入线程
                    this.nowGameScene.StopThread();
                    Game.ChangeScene(E_SceneType.End);
                    return;
                   
                }
                // 添加动态墙壁计数
                //根据索引来得到行
                recordInfo[h - 1 - walls[i].Position.y] += 1;
            }
            // 先把之前的动态小方块擦掉

            ClearDraw();
            CheckClear();
            IDraw();

        }
        public bool cleared = false;
        public void CheckClear()
        {
            // 添加一个待移除记录列表
            List<DrawObject> tmpClearList = new List<DrawObject>();
            //要选择记录行中有多少个方块的容器
            //数组
            //判断这个一行是否满(方块)
            //遍历数组 检测数组里面存的数
            //是不是w-2

            for (int i = 0; i < recordInfo.Length; i++)
            {
                // 满足条件才证明满了
                if (recordInfo[i] == w)
                {
                   
                    // 这一行的虽有小方块移除
                    for (int j = 0; j < dynamicWalls.Count; j++)
                    {
                        //当前动态方块y计算在那一行,如果行号和当前所以一直就证明应该移除
                        if (i == (h - dynamicWalls[j].Position.y-1))
                        {
                            // 移除
                            // 添加一个移除记录列表
                            tmpClearList.Add(dynamicWalls[j]);
                        }
                        //要这一行上的所有小方块下移一个单位
                        else if ((h - dynamicWalls[j].Position.y - 1)>i)
                        {
                            ++dynamicWalls[j].Position.y;
                        }
                    }
                    //移除待移除的小方块
                    for (int j = 0; j < tmpClearList.Count; j++)
                    {
                        dynamicWalls.Remove(tmpClearList[j]);           
                    }
                    //记录小方块的数量数组从上到下迁移
                    for (int j = i; j < recordInfo.Length-1; j++)
                    {
                        recordInfo[j] = recordInfo[j + 1];
                    }
                    // 置空最顶的计数
                    recordInfo[recordInfo.Length - 1] = 0;

                    // 垮掉一行ScoreTime+1
                   
                    // 垮掉一行后,再次从头判断一次
                    cleared =true;                 
                    CheckClear();
                    break;
                }
            }
        }

        

    }
}

BlockInfo

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 俄罗斯方块.方块信息类
{
    internal class BlockInfo
    {
        // 方块信息坐标
        private List<Position[]> list;
        public BlockInfo(E_DrawType type) {
            //list 初始化才能装东西
            list = new List<Position[]>();
            switch (type)
            {
              
                case E_DrawType.Cube:
                    // list添加形状信息
                    list.Add(new Position[3] {

                    new Position(2,0),
                        new Position(0,1),
                        new Position(2,1)
                    });
                    break;
                case E_DrawType.Line:
                    list.Add(new Position[3] {

                    new Position(0, -1),
                    new Position(0, 1),
                    new Position(0, 2),
                   
                    });
                    list.Add(new Position[3] {

                   new Position(-4, 0),
                    new Position(-2, 0),
                    new Position(2, 0),


                    });
                    list.Add(new Position[3] {

                    new Position(0, -2),
                    new Position(0, -1),
                    new Position(0, 1),

                    });
                    list.Add(new Position[3] {

                    new Position(-2, 0),
                    new Position(2, 0),
                    new Position(4, 0),

                    });
                    break;
                case E_DrawType.Tank:
                    list.Add(new Position[3] {

                    new Position(-2, 0),
                    new Position(2, 0),
                    new Position(0, 1),

                    });
                    list.Add(new Position[3] {

                    new Position(0,-1),
                        new Position(-2,0),
                        new Position(0,1)

                    });
                    list.Add(new Position[3] {

                    new Position(0,-1),
                        new Position(-2,0),
                        new Position(2,0)

                    });
                    list.Add(new Position[3] {

                   new Position(0,-1),
                        new Position(2,0),
                        new Position(0,1)

                    });

                    break;
                case E_DrawType.Left_Ladder:
                    list.Add(new Position[3]{
                        new Position(0,-1),
                        new Position(2,0),
                        new Position(2,1)
                    });
                    list.Add(new Position[3]{
                        new Position(2,0),
                        new Position(0,1),
                        new Position(-2,1)
                    });
                    list.Add(new Position[3]{
                       new Position(-2,-1),
                        new Position(-2,0),
                        new Position(0,1)
                    });
                    list.Add(new Position[3]{
                        new Position(0,-1),
                        new Position(2,-1),
                        new Position(-2,0)
                    });
                    break;
                case E_DrawType.Right_Ladder:
                    list.Add(new Position[3]{
                        new Position(0,-1),
                        new Position(-2,0),
                        new Position(-2,1)
                    });
                    list.Add(new Position[3]{
                        new Position(-2,-1),
                        new Position(0,-1),
                        new Position(2,0)
                    });
                    list.Add(new Position[3]{
                        new Position(2,-1),
                        new Position(2,0),
                        new Position(0,1)
                    });
                    list.Add(new Position[3]{
                        new Position(0,1),
                        new Position(2,1),
                        new Position(-2,0)
                    });
                    break;
                case E_DrawType.Left_Long_Ladder:
                    list.Add(new Position[3]{
                        new Position(-2,-1),
                        new Position(0,-1),
                        new Position(0,1)
                    });
                    list.Add(new Position[3]{
                        new Position(2,-1),
                        new Position(-2,0),
                        new Position(2,0)
                    });
                    list.Add(new Position[3]{
                        new Position(0,-1),
                        new Position(2,1),
                        new Position(0,1)
                    });
                    list.Add(new Position[3]{
                        new Position(2,0),
                        new Position(-2,0),
                        new Position(-2,1)
                    });
                    break;
                case E_DrawType.Right_Long_Ladder:
                    list.Add(new Position[3]{
                        new Position(0,-1),
                        new Position(0,1),
                        new Position(2,-1)
                    });
                    list.Add(new Position[3]{
                        new Position(2,0),
                        new Position(-2,0),
                        new Position(2,1)
                    });
                    list.Add(new Position[3]{
                        new Position(0,-1),
                        new Position(-2,1),
                        new Position(0,1)
                    });
                    list.Add(new Position[3]{
                        new Position(-2,-1),
                        new Position(-2,0),
                        new Position(2,0)
                    });
                    break;
                default:
                    break;
            }
        }  

        /// <summary>
        ///  提供给外部啊根据索引位置快速获取位置偏移信息
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public Position[] this[int index]
        {
            get {
                if (index < 0)
                {
                    return list[0];

                }
                else if (index >= list.Count)
                {
                    return list[list.Count - 1];
                }
                else
                    return list[index];
            }
        }
        public int Count { get => list.Count; }
    }
}


Score.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace 俄罗斯方块
{
    // 画出得分
    internal class Score : Map
    {
        protected String SumScoreName;
        protected String ScoreLineName;

        public int ScoreNum;
        public int ScoreLine;
        int ScoreInit = 0;
        int LineInit = 0;
        public Score(GameScene scene) : base(scene)
        {
            // 显示得分位置,地图边界在map里面
            SumScoreName = "分数:";
            ScoreLineName = "行数:";

            #region 计算得分
            // 消除一行得分+10,行数+1
            #endregion
            Console.SetCursorPosition(2, Game.h - 4);
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write(SumScoreName);
            Console.SetCursorPosition(2, Game.h - 3);
            Console.ForegroundColor = ConsoleColor.Red;
            Console.Write(ScoreLineName);
            // 初始化分数0 
            Console.SetCursorPosition(7, Game.h - 4);
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write(ScoreInit);
            Console.SetCursorPosition(7, Game.h - 3);
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write(LineInit);

        }
        public void DrawScore(int ScoreNum, int ScoreLine)
        {
            // 显示实际得分
            Console.SetCursorPosition(7, Game.h - 4);
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write(ScoreNum);
            Console.SetCursorPosition(7, Game.h - 3);
            Console.ForegroundColor = ConsoleColor.White;
            Console.Write(ScoreLine);
        }
        public int updateScore()
        {
            return ++ScoreCount;
        }
        public int GetScore()
        {
            return Score += 10;
        }

        // 判断是否消除一行
        public void Isclear()
        {
            DrawScore(GetScore(), updateScore());
        }



    }

}

BlockWorker

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
using 俄罗斯方块.方块信息类;

namespace 俄罗斯方块
{

    enum E_Change_Type
    {

        left,
        right,

    }
    internal class BlockWorker : IDraw
    {
        // 方块们
        private List<DrawObject> blocks;
        // 记录各个方块的信息
        private Dictionary<E_DrawType, BlockInfo> blockInfoDict;

        private BlockInfo nowBlockInfo;
        // 当前形态索引
        private int nowInfoIndex;
        public BlockWorker()
        {

            // 初始化撞开信息
            blockInfoDict = new Dictionary<E_DrawType, BlockInfo>()
            {
                { E_DrawType.Cube ,new BlockInfo(E_DrawType.Cube)},
                { E_DrawType.Line ,new BlockInfo(E_DrawType.Line)},
                { E_DrawType.Tank ,new BlockInfo(E_DrawType.Tank)},
                { E_DrawType.Left_Ladder ,new BlockInfo(E_DrawType.Left_Ladder)},
                { E_DrawType.Right_Ladder ,new BlockInfo(E_DrawType.Right_Ladder)},
                { E_DrawType.Left_Long_Ladder ,new BlockInfo(E_DrawType.Left_Long_Ladder)},
                { E_DrawType.Right_Long_Ladder ,new BlockInfo(E_DrawType.Right_Long_Ladder)},
            };
            // 随机方块
            RandomCratBlock();

        }

        public void IDraw()
        {
            for (int i = 0; i < blocks.Count; i++)
            {
                blocks[i].Draw();
            }
        }

        public void RandomCratBlock()
        {
            // 随机方块的类型
            Random r = new Random();
            E_DrawType type = (E_DrawType)r.Next(1, 8);
            // 每次创建一个砖块其实就是创建4个小方形
            blocks = new List<DrawObject>()
            {
                new DrawObject(type),
                new DrawObject(type),
                new DrawObject(type),
                new DrawObject(type),

            };
            // 初始化方块的位置
            blocks[0].Position = new Position(24, -5);
            // 把方块信息存起来

            nowBlockInfo = blockInfoDict[type];
            // 随机形态
            nowInfoIndex = r.Next(0, nowBlockInfo.Count);
            // 取出一种形态坐标信息
            Position[] positions = nowBlockInfo[nowInfoIndex];
            for (int i = 0; i < positions.Length; i++)
            {
                // 取出来的pos相对原点方块的坐标
                blocks[i + 1].Position = blocks[0].Position + positions[i];

            }

        }

        #region 变形相关
        // 清除
        public void ClearDraw()
        {
            for (int i = 0; i < blocks.Count; i++)
            {
                blocks[i].ClearDraw();

            }


        }

        public void Change(E_Change_Type type)
        {
            // 变之前擦除
            ClearDraw();
            switch (type)
            {
                case E_Change_Type.left:
                    --nowInfoIndex;
                    if (nowInfoIndex < 0)
                    {
                        nowInfoIndex = nowBlockInfo.Count - 1;

                    }

                    break;
                case E_Change_Type.right:
                    ++nowInfoIndex;
                    if (nowInfoIndex >= nowBlockInfo.Count)
                    {
                        nowInfoIndex = 0;
                    }
                    break;
                default:
                    break;
            }
            // 得到索引目的是得到对应形态的位置偏移信息
            //用于设置另外三个小方块
            Position[] positions = nowBlockInfo[nowInfoIndex];
            for (int i = 0; i < positions.Length; i++)
            {
                // 取出来的pos相对原点方块的坐标
                blocks[i + 1].Position = blocks[0].Position + positions[i];

            }

            // 变之后再绘制
            IDraw();
        }

        public bool IsChange(E_Change_Type type, Map map)
        {
            int nowIndex = nowInfoIndex;
            switch (type)
            {
                case E_Change_Type.left:
                    --nowIndex;
                    if (nowIndex < 0)
                    {
                        nowIndex = nowBlockInfo.Count - 1;

                    }

                    break;
                case E_Change_Type.right:
                    ++nowIndex;
                    if (nowIndex >= nowBlockInfo.Count)
                    {
                        nowIndex = 0;
                    }
                    break;
                default:
                    break;
            }
            // 通过临时索引判断是否超出边界
            Position[] Nowpositions = nowBlockInfo[nowIndex];
            // 判断是否超出地图边界
            Position tempPostion;
            for (int i = 0; i < Nowpositions.Length; i++)
            {
                // 判断左右边界和下边界
                tempPostion = blocks[0].Position + Nowpositions[i];
                if (tempPostion.x < 2 || tempPostion.x >= Game.w - 2 || tempPostion.y >= map.h)
                {
                    return false;
                }
            }
            // 判断是否和地图上的动态方块重合
            for (int i = 0; i < Nowpositions.Length; i++)
            {
                tempPostion = blocks[0].Position + Nowpositions[i];
                for (int j = 0; j < map.dynamicWalls.Count; j++)
                {
                    if (tempPostion == map.dynamicWalls[j].Position)
                    {
                        return false;
                    }

                }

            }

            return true;

        }

        #endregion

        #region 方块左右移动
        /// <summary>
        /// 方块左右移动函数
        /// </summary>
        /// <param name="type"></param>
        public void MoveRl(E_Change_Type type)
        {
            // 动之前得到坐标擦除
            ClearDraw();
            // 根据传入的类型,决定左右移动
            // 左 x-2,y0,右x+2,y0
            Position movePosition = new Position(type == E_Change_Type.left ? -2 : 2, 0);
            for (int i = 0; i < blocks.Count; i++)
            {

                blocks[i].Position += movePosition;

            }
            IDraw();

        }
        // 判断是否可以移动
        public bool IsMoveRL(E_Change_Type type, Map map)
        {
            // 判断左右边界重合
            // 得到左右的偏移位置
            Position movePosition = new Position(type == E_Change_Type.left ? -2 : 2, 0);
            // 动过之后结果不能直接改小方块的位置
            Position position;
            for (int i = 0; i < blocks.Count; i++)
            {
                position = blocks[i].Position + movePosition;
                if (position.x < 2 || position.x >= Game.w - 2)
                {
                    return false;
                }
            }
            // 要不要和动态方块重合

            for (int i = 0; i < blocks.Count; i++)
            {
                position = blocks[i].Position + movePosition;

                for (int j = 0; j < map.dynamicWalls.Count; j++)
                {
                    if (position == map.dynamicWalls[j].Position)
                    {
                        return false;
                    }
                }
            }
            return true;
        }
        #endregion

        #region 自动向下移动
        /// <summary>
        /// 自动移动
        /// </summary>
        public void AutoMoveDown()
        {
            ClearDraw();
            Position downMove = new Position(0, 1);
            for (int i = 0; i < blocks.Count; i++)
            {
                blocks[i].Position += downMove;
                //blocks[i].Position.y += 1;

            }
            IDraw();
        }
        public bool IsAutoMove(Map map)
        {
            // 用临时变量存储下一次移动的位置用于重合判断
            Position position;
            Position downMove = new Position(0, 1);

            for (int i = 0; i < blocks.Count; i++)
            {
                position = blocks[i].Position + downMove;
                if (position.y >= map.h)
                {
                    // 停下
                    map.AddWall(blocks);
                    // 随机创建新的方块
                    RandomCratBlock();
                    return false;
                }
            }
            // 动态方块
            for (int i = 0; i < blocks.Count; i++)
            {
                position = blocks[i].Position + downMove;
                for (int j = 0; j < map.dynamicWalls.Count; j++)
                {
                    if (position == map.dynamicWalls[j].Position)
                    {
                        // 停下
                        map.AddWall(blocks);
                        // 随机创建新的方块
                        RandomCratBlock();
                        return false;
                    }
                }
            }
            return true;
        }
        #endregion


    }
}

DrawObject

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 俄罗斯方块
{
    /// <summary>
    /// 绘制方块
    /// </summary>
    enum E_DrawType
    {

        /// <summary>
        /// 墙壁
        /// </summary>
        wall,
        /// <summary>
        /// 正方形
        /// </summary>
        Cube,
        /// <summary>
        /// 长的
        /// </summary>
        Line,
        /// <summary>
        /// 坦克
        /// </summary>
        Tank,
        /// <summary>
        /// 左
        /// </summary>
        Left_Ladder,
        /// <summary>
        /// 右
        /// </summary>
        Right_Ladder,
        /// <summary>
        /// 左长
        /// </summary>
        Left_Long_Ladder,
        /// <summary>
        /// 右长
        /// </summary>
        Right_Long_Ladder,


    }
    internal class DrawObject : IDraw
    {
        public Position Position;
        public E_DrawType type;

        public DrawObject(E_DrawType type)
        {
            this.type = type;
        }

        public DrawObject(E_DrawType type, int x, int y) : this(type)
        {
            this.Position = new Position(x, y);
        }


        public void Draw()
        {
            if (Position.y<0)
            {
                return;
            }

            Console.SetCursorPosition(Position.x, Position.y);
            switch (type)
            {
                case E_DrawType.wall:
                    Console.ForegroundColor = ConsoleColor.Red;
                    break;
                case E_DrawType.Cube:
                    Console.ForegroundColor = ConsoleColor.Green;
                    break;
                case E_DrawType.Line:
                    Console.ForegroundColor = ConsoleColor.Blue;

                    break;
                case E_DrawType.Tank:
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    break;
                case E_DrawType.Left_Ladder:
                    Console.ForegroundColor = ConsoleColor.DarkGray;
                    break;
                case E_DrawType.Right_Ladder:
                    Console.ForegroundColor = ConsoleColor.DarkGray;

                    break;
                case E_DrawType.Left_Long_Ladder:
                    Console.ForegroundColor = ConsoleColor.Gray;
                    break;
                case E_DrawType.Right_Long_Ladder:
                    Console.ForegroundColor = ConsoleColor.Gray;

                    break;
                default:
                    break;
            }
            Console.Write("卐");
        }
      
        public void ClearDraw()
        {
            if (Position.y<0)
            {
                return;
            }
            Console.SetCursorPosition(Position.x, Position.y);
            Console.Write("  ");
        }

        /// <summary>
        /// 切换方块类型,搬砖掉落到墙壁上转为墙壁
        /// </summary>
        public void ChangType(E_DrawType type)
        {
            this.type = type;

        }
        void IDraw.IDraw()
        {
            throw new NotImplementedException();
        }
    }
}

IDraw

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 俄罗斯方块
{
    internal interface IDraw
    {
        void IDraw();
    }
}

Position

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 俄罗斯方块
{
    internal class Position
    {
        public int x;
        public int y;
        public Position(int x, int y)
        {
            this.x = x;
            this.y = y;
        }


        public static bool operator ==(Position p1, Position p2) {
            if (p1.x== p2.x&&p1.y==p2.y)
            {
                return true;
            }
            return false;
        }
        public static bool operator !=(Position p1, Position p2)
        {
            if (p1.x == p2.x && p1.y == p2.y)
            {
                return false;
            }
            return true;
        }
        public static Position operator +(Position p1, Position p2)
        {
            Position position = new Position(p1.x+p2.x,p1.y+p2.y);
            return position;
        }
    }

}

inputThread

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace 俄罗斯方块
{
    internal class inputThread
    {

        Thread inputThead;

        public event Action inputEvent;

        private static inputThread instance = new inputThread();
        public static inputThread Instance
        {
            get { return instance; }
        }
        private  inputThread()
        {
            inputThead = new Thread(InputCheck);
            inputThead.IsBackground = true;
            inputThead.Start();



        }

        public void InputCheck() {
            while (true)
            {
                inputEvent?.Invoke();
            }
        }
        // 线程

    }
}

显示清除最后

image-20250118174044510

posted @ 2025-01-19 00:16  #卧龙先生#  阅读(43)  评论(0)    收藏  举报