C#--进阶

CSharp进阶知识点学习

知识点汇总

简单数据结构类:

Lesson1:ArrayList

练习:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lesson1_练习
{

    #region 练习一
    //请简述ArrayList 和 数组的区别

    //1.ArrayList本质上是一个object数组的封装
    //2.数组可以指定存储类型,ArrayList默认为object类型
    //3.数组的增删查改需要我们自己去实现,ArrayList帮我们封装好了方便的API来使用
    //4.ArrayList使用时可能存在装箱拆箱,数组使用时只要不是object数组就不存在这个问题
    //5.数组长度用Length,ArrayList长度用Count

    #endregion

    #region 练习二
    //创建一个背包管理类,使用ArrayList存储物品
    //实现购买物品,卖出物品,显示物品的功能,购买与卖出物品会导致金钱变化

    class BagMgr
    {
        //背包中的物品
        private ArrayList items;

        private int money;

        public BagMgr(int money)
        {
            this.money = money;
            items = new ArrayList();
        }

        //买物品
        public void BuyItem(Item item)
        {
            //避免乱传
            if (item.num <= 0 || item.money < 0)
            {
                Console.WriteLine("请传入正确的物品信息。");
                return;
            }

            if (money < item.money * item.num)
            {
                Console.WriteLine("买不起,钱不够。");
                return; 
            }

            //若是钱够就减钱
            money -= item.money * item.num;
            Console.WriteLine("购买了{0},共{1}个花费了{2}元",item.name, item.num, item.money*item.num);
            Console.WriteLine("剩余{0}钱",money);

            //如果想要叠加物品 可以在前面先判断 是否有这个物品 然后加数量
            for (int i = 0; i < items.Count; i++)
            {
                if ((items[i] as Item).id == item.id)
                {
                    (items[i] as Item).num += item.num;
                    return;
                }
            }
            //把一组物品加到 list中
            items.Add(item);
        }

        //卖出物品
        public void SellItem(Item item)
        {
            for (int i = 0; i < items.Count; i++)
            {
                //如何判断 卖的东西背包里有没有
                //这是在判断 两个引用地址 指向的是不是同一个房间地址
                //所以我们要判断 卖的物品 一般不这样判断
                //if ((items[i] as Item) == item)
                //{

                //}

                if ((items[i] as Item).id == item.id)
                {
                    //两种情况 
                    int num = 0;
                    string name = (items[i] as Item).name;
                    int money = (items[i] as Item).money;
                    if ((items[i] as Item).num > item.num)
                    {
                        //1.比我身上的少
                        num = item.num;
                        (items[i] as Item).num -= num;
                    }
                    else
                    {
                        //2.大于等于我身上的东西数量 
                        num = (items[i] as Item).num;
                        //卖完了就移除
                        items.RemoveAt(i);
                    }

                    int sellMoney = (int)(num * money * 0.8f);
                    money += sellMoney;

                    Console.WriteLine("卖出去了{0},共{1}个,赚了{2}元钱", name, num, sellMoney);
                    Console.WriteLine("目前拥有{0}元钱",money);

                    return;

                }

            }
            
        }

        public void SellItem(int id, int num)
        {
            //调用一下上面的方法
            Item item = new Item(id, num);
            SellItem(item);
        }

        public void SellItem(string name)
        {

        }

        //显示物品
        public void ShowItem()
        {
            Item item;
            for (int i = 0; i < items.Count; i++)
            {
                item = items[i] as Item;
                Console.WriteLine("{0}    {1}个", item.name, item.num);
            }
            Console.WriteLine("当前拥有{0}元钱", money);
        }
    }

    class Item
    {
        //物品唯一ID 来区分物品的种类
        public int id;
        //物品值多少钱(表示单价)
        public int money; 
        //物品名字
        public string name;
        //物品数量
        public int num;

        public Item(int id, int num)
        {
            this.id = id;
            this.num = num;
        }

        public Item(int id, int money, string name, int num)
        {
            this.id = id;
            this.money = money;
            this.name = name;
            this.num = num;
        }

    }

    #endregion

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

            BagMgr bag = new BagMgr(99999);
            Item i1 = new Item(1, 10, "痛苦面具", 10);
            Item i2 = new Item(2, 20, "三圣之力", 5);
            Item i3 = new Item(3, 666, "狂徒铠甲", 8);

            bag.BuyItem(i1);
            bag.BuyItem(i2);
            bag.BuyItem(i3);

            bag.SellItem(i2);

            bag.SellItem(3, 2);
            bag.SellItem(3, 3);

            bag.ShowItem();

        }
    }
}

Lesson2:Stack

练习:

Lesson3:Queue(队列)

练习:

Lesson4:Hashtable

练习:

用到了单例模式

泛型:

Lesson5:泛型

练习:

Lesson6:泛型约束

练习:

常用泛型数据结构类

Lesson7:List

练习:

Lesson8:Dictionary

练习:

Lesson9:顺序存储和链式存储

知识点六 顺序存储和链式存储的优缺点

练习:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

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

            #region 练习一
            //请说出常用的数据结构有哪些
            //数组、栈、队列、树、链表、散列表、堆、图
            #endregion

            #region 练习二
            //请描述顺序存储和链式存储的区别
            //顺序存储:内存中用一组地址连续的存储单元存储线性表(连续地址存储)
            //链式存储:内存中用一组任意的存储单元存储线性表(任意地址存储)
            #endregion

            #region 练习三
            //请尝试自己实现一个双向链表
            //并提供以下方法和属性
            //数据的个数,头节点,尾节点
            //增加数据到链表最后
            //删除指定位置节点

            LinkedList<int> list = new LinkedList<int>();
            list.Add(1);
            list.Add(2);
            list.Add(3);
            list.Add(4);
            list.Add(5);

            //从头遍历
            LinkedNode<int> node = list.Head;
            for (int i = 0; i < list.Num; i++)
            {
                Console.WriteLine(node.value);
                node = node.nextNode;
            }

            Console.WriteLine("=======================");

            //从尾遍历
            node = list.Last;
            for (int i = 0; i < list.Num; i++)
            {
                Console.WriteLine(node.value);
                node = node.frontNode;
            }

            Console.WriteLine("=======================");

            list.Remove(2);
            node = list.Head;
            for (int i = 0; i < list.Num; i++)
            {
                Console.WriteLine(node.value);
                node = node.nextNode;
            }

            Console.WriteLine("=======================");

            list.Remove(0);
            node = list.Head;
            for (int i = 0; i < list.Num; i++)
            {
                Console.WriteLine(node.value);
                node = node.nextNode;
            }

            Console.WriteLine("=======================");

            list.Remove(3);
            node = list.Head;
            for (int i = 0; i < list.Num; i++)
            {
                Console.WriteLine(node.value);
                node = node.nextNode;
            }

            #endregion


        }
    }

    /// <summary>
    /// 双向列表节点
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class LinkedNode<T>
    {
        public T value;
        //存储上一个是谁
        public LinkedNode<T> frontNode;
        //存储下一个是谁
        public LinkedNode<T> nextNode;

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

    /// <summary>
    /// 双向链表类 管理节点 管理添加
    /// </summary>
    /// <typeparam name="T"></typeparam>
    class LinkedList<T>
    {
        private int num = 0;
        private LinkedNode<T> head;
        private LinkedNode<T> last;

        public int Num
        {
            get
            {
                return num;
            }
        }

        public LinkedNode<T> Head
        {
            get
            {
                return head;
            }
        }

        public LinkedNode<T> Last
        {
            get
            {
                return last;
            }
        }

        public void Add(T value)
        {
            LinkedNode<T> node = new LinkedNode<T>(value);
            if (head == null)
            {
                head = node;
                last = node;
            }
            else
            {
                //添加到尾部
                last.nextNode = node;
                //尾部添加记入上一个节点是谁
                node.frontNode = last;
                //让当前添加的变成最后一个节点
                last = node;
            }

            ++num;
        }

        public void Remove(int num)
        {
            //首先判断有没有越界
            if (this.num - 1 < num || num < 0)
            {
                Console.WriteLine("不存在这个位置的值");
                return;
            }
            int temp = 0;
            LinkedNode<T> tempNode = head;
            while (true)
            {
                if (temp == num)
                {
                    //找到了,移除即可
                    //当前要移除的节点的上一个节点 指向自己的下一个节点
                    if (tempNode.frontNode != null)
                    {
                        tempNode.frontNode.nextNode = tempNode.nextNode;
                    }
                    if (tempNode.nextNode != null)
                    {
                        tempNode.nextNode.frontNode = tempNode.frontNode;
                    }
                    //如果是头节点需要改变头节点的指向
                    if (num == 0)
                    {
                        //如果头结点被移除 那头节点就变成了头节点的下一个
                        head = tempNode.nextNode;
                    }
                    else if(num == this.num - 1)
                    {
                        //如果尾节点被移除了,那么尾节点就变成了尾节点的上一个
                        last = last.frontNode;
                    }

                    --this.num;
                    break;
                }
                ++temp;
                tempNode = tempNode.nextNode;
            }
        }
    }

}

Lesson10:LinkedList

练习:

重点:

Lesson11:泛型栈和队列

练习:
数组、List、Dictionary、Stack、Queue、LinkedList
这些存储容器,对于我们来说应该如何选择?

委托和事件

Lesson12:委托

练习:

Lesson13:事件

事件和委托的区别

Lesson14:匿名函数

练习:

Lesson15:Lambad表达式

补充知识点:

练习:

List排序

Lesson16:List排序

练习:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Lesson16_练习
{

    #region 练习一
    //写一个怪物类,创建10个怪物将其添加到List中
    //对List列表进行排序,根据用户输入数字进行排序
    //1.攻击排序
    //2.防御排序
    //3.血量排序
    //4.反转

    class Monster : IComparable<Monster>
    {
        public int atk;
        public int enk;
        public int hp;
        public int id;

        public Monster(int atk, int enk, int hp, int id)
        {
            this.atk = atk;
            this.enk = enk;
            this.hp = hp;
            this.id = id;
        }

        //继承接口的排序方法
        public int CompareTo(Monster other)
        {
            //升序
            if (this.atk > other.atk)
            {
                return 1;
            }
            else
            {
                return -1;
            }
        }
    }

    #endregion

    #region 练习二
    //写一个物品类(类型、名字、品质),创建10个物品
    //添加到List中
    //同时使用类型、品质、名字长度进行比较
    //排序的权重是:类型>品质>名字长度

    class Item
    {
        public int type;
        public string name;
        public int quality;

        public Item(int type, string name, int quality)
        {
            this.type = type;
            this.name = name;
            this.quality = quality;
        }

        public override string ToString()
        {
            return string.Format("道具信息:类型:{0},名字:{1},品质:{2}", type, name, quality);
        }
    }

    #endregion

    #region 练习三
    //涉及到 : linq  SQL

    //尝试利用List排序方式对Dictionary中的内容排序
    //提示:得到Dictionary的所有键值对信息存入List中
    #endregion


    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("List排序练习");

            #region 练习一
            Console.WriteLine("================  练习一  ===================");
            List<Monster> m = new List<Monster>();
            m.Add(new Monster(5, 1, 9, 1));
            m.Add(new Monster(4, 2, 8, 2));
            m.Add(new Monster(3, 1, 7, 3));
            m.Add(new Monster(2, 4, 3, 4));
            m.Add(new Monster(3, 8, 3, 5));
            m.Add(new Monster(8, 7, 5, 6));
            m.Add(new Monster(4, 1, 7, 7));
            m.Add(new Monster(7, 2, 6, 8));
            m.Add(new Monster(2, 9, 6, 9));
            m.Add(new Monster(6, 4, 7, 10));

            Console.WriteLine("请输入1~3:");
            int num = int.Parse(Console.ReadLine());

            try
            {
                if (num == 1)
                {
                    //继承接口的方式
                    m.Sort();
                    Console.WriteLine("攻击力升序排序:");
                    for (int i = 0; i < m.Count; i++)
                    {
                        Console.WriteLine("怪物:{0},攻击力为:{1}", m[i].id, m[i].atk);
                    }
                }
                else if (num == 2)
                {
                    //调用方法--泛型的方式
                    m.Sort(Test);
                    Console.WriteLine("防御力降序排序:");
                    for (int i = 0; i < m.Count; i++)
                    {
                        Console.WriteLine("怪物:{0},防御力为:{1}", m[i].id, m[i].enk);
                    }
                }
                else if (num == 3)
                {
                    //匿名函数的方法  升序
                    //m.Sort(delegate (Monster m1, Monster m2)
                    //{
                    //    return m1.hp > m2.hp ? 1 : -1;
                    //});
                    //lambad  降序
                    m.Sort((m1, m2) => { return m1.hp > m2.hp ? -1 : 1; });
                    Console.WriteLine("血量升序排序:");
                    for (int i = 0; i < m.Count; i++)
                    {
                        Console.WriteLine("怪物:{0},血量为:{1}", m[i].id, m[i].hp);
                    }
                }
                else if (num == 4)
                {
                    m.Reverse();
                    Console.WriteLine("反转打印");
                    for (int i = 0; i < m.Count; i++)
                    {
                        Console.WriteLine("怪物:{0}", m[i].id);
                    }
                }
            }
            catch
            {
                Console.WriteLine("请输入数字!");
            }
            #endregion

            #region 练习二
            Console.WriteLine("================  练习二  ===================");
            List<Item> itemList = new List<Item>();
            Random r = new Random();
            for (int i = 0; i < 10; i++)
            {
                itemList.Add(new Item(r.Next(1, 6), "Item" + r.Next(1, 201), r.Next(1, 6)));
                Console.WriteLine(itemList[i]);
            }

            itemList.Sort((a, b) =>
            {
                //类型不同 按类型比
                if (a.type != b.type)
                {
                    return a.type > b.type ? -1 : 1;
                }
                //品质不同 按品质比
                else if (a.quality != b.quality)
                {
                    return a.quality > b.quality ? -1 : 1;
                }
                //否则就直接按名字长度比
                else
                {
                    return a.name.Length > b.name.Length ? -1 : 1;
                }
            });

            Console.WriteLine("=========================");

            for (int i = 0; i < 10; i++)
            {
                Console.WriteLine(itemList[i]);
            }
            #endregion

            #region 练习三
            Console.WriteLine("================  练习三  ===================");
            Dictionary<int, string> dic = new Dictionary<int, string>();
            dic.Add(1, "123456");
            dic.Add(4, "123456");
            dic.Add(3, "123456");
            dic.Add(7, "123456");
            dic.Add(5, "123456");
            dic.Add(9, "123456");

            List<KeyValuePair<int, string>> list = new List<KeyValuePair<int, string>>();

            foreach (KeyValuePair<int, string> item in dic)
            {
                list.Add(item);
                Console.WriteLine(item.Key + "--" + item.Value);
            }

            list.Sort((a, b) =>
            {
                return a.Key > b.Key ? 1 : -1;
            });

            for (int i = 0; i < list.Count; i++)
            {
                Console.WriteLine(list[i].Key + "--" + list[i].Value);
            }

            #endregion


        }



        static int Test(Monster m1, Monster m2)
        {
            //降序
            if (m1.enk > m2.enk)
            {
                return -1;
            }
            else
            {
                return 1;
            }
        }

    }
}

协变逆变

Lesson17:协变和逆变

练习:
请描述协变逆变有什么作用

多线程

Lesson18:多线程

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

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

    }
}

练习:

预处理器指令

Lesson19:预处理器指令

//定义一个符号
#define Unity4
#define Unity5
//#define Unity2017
#define Unity2019
//取消定义一个符号
#undef Unity4

#define IOS
#define Android
#define PC

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

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中会用来进行一些平台或者版本的判断
            //决定不同的版本或者不同的平台使用不同的代码逻辑

        }
    }
}

练习:
#define Unity5
#define Unity2017
#define Unity2020
#undef Unity2017
#undef Unity5

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

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

            #region 练习一
            //请说出至少4种预处理器指令
            //1.#define  定义一个符号 (没有值的变量)
            //  #undef   取消定义一个符号

            //2.#if
            //  #elif
            //  #else
            //  #endif

            //3.#warning
            //  #error

            #endregion

            #region 练习二
            //请使用预处理器指令实现
            //写一个函数计算两个数
            //当是Unity5版本时算加法
            //当是Unity2017版本时算乘法
            //当是Unity2020版本是算减法
            //都不是返回0

            Console.WriteLine(Test(10,20));


            #endregion

        }

        static int Test(int x, int y)
        {
#if Unity5
            int sam = x + y;
            return sam;
#elif Unity2017
            int sam = x * y;
            return sam;
#elif Unity2020
            int sam = x - y;
            return sam;
#else 
            return 0;

#endif
        }

    }
}

反射和特性

Lesson20:反射

练习:

注意:路径最后加上后缀,不然会报错,暂时我也不知道为什么不加会报错

Lesson21:特性

#define Fun
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

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("123456");

            Fun();

            #endregion


            //总结
            //特性是用于 为元数据再添加更多的额外信息(变量、方法等等)
            //我们可以通过反射获取这些额外的数据 来进行一些特殊的处理
            //自定义特性--继承Attribute类

            //系统自带特性:过时特性

            //为什么要学习特性
            //Unity引擎中许多地方都用到了特性来进行一些特殊处理

        }
    }
}

练习:

Lesson22:迭代器

练习:

Lesson23:特殊语法

Lesson24:值和引用

排序进阶

面试时会考,但后面开发不常用

建议面试时突击复习下就可以!

Lesson25:插入排序

Lesson26:希尔排序

暂时学到这,剩余排序进阶的知识后面再学!

总结

毋庸置疑C#进阶的知识点挺重要,要多回来复习学习!

接下来的任务是把C#进阶的实践小项目《俄罗斯方块》做完,然后进入Unity的学习!

抓紧时间!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/460304.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

新手如何选择货币对进行交易。昂首资本给一个参考

通过上篇文章&#xff0c;各位投资者都知道货币对总共分为三组&#xff1a;主要货币对、交叉货币对和新兴市场货币对和异国货币对。但是哪种货币对对我们新手友好&#xff1f;这个很多投资者不清楚&#xff0c;今天昂首资本给各位投资者一个参考。 各位投资者应该知道每种货币…

SoloX - Android/iOS性能数据实时采集工具

文章目录 一、简介二、环境要求三、安装部署四、使用方法4.1 通过浏览器直接使用4.2 使用Python收集4.3 使用API收集 一、简介 SoloX是一个可以实时收集Android/iOS性能数据的web工具。 快速定位分析性能问题&#xff0c;提升应用的性能和品质。 无需ROOT/越狱&#xff0c;即插…

媒体播放器及媒体服务器软件Plex

什么是 Plex &#xff1f; Plex 是一套媒体播放器及媒体服务器软件&#xff0c;让用户整理在设备上的有声书、音乐、播客、图片和视频文件&#xff0c;并通过流式传输至移动设备、智能电视和电子媒体播放器上。Plex 可用于 Windows、Android、Linux、OS X和 FreeBSD。 在接触 N…

目标检测——FPN与DSSD算法解读

由于FPN和DSSD网络结构比较相似&#xff0c;且发布时间非常相近&#xff0c;所以放一起解读 按时间来算FPN是先于DSSD在arxiv上发布的&#xff0c;FPN第一版是2016年12月9日&#xff0c;DSSD第一版是2017年1月23日&#xff0c;前后相差一个月。 YOLO系列其他文章&#xff1a; …

单片机设计-超声波视力保护仪的设计与实现

项目介绍 技术&#xff1a;C语言、单片机等 本设计利用超声波技术检测眼睛与书本的距离&#xff0c;调整看书位置&#xff0c;通过光敏检测判断环境光线强度是否适合阅读&#xff0c;并通过定时器设定阅读时长&#xff0c;以此解决人们由于看书姿势的错误&#xff0c;阅读环境…

C/C++ Socket 获取或设置 TCP MSS 大小

通过 Socket 系统接口&#xff0c;链接到一个TCP服务器&#xff0c;那么在链接成功之后会被配置一个从本地端到目的端最佳的TCP_MSS大小。 我们通过这个特点&#xff0c;即可轻松的实现&#xff0c;链路MTU大小发现功能&#xff0c;在不依赖ROOT管理员权限的情况下&#xff0c;…

【数据结构取经之路】建堆堆排序

目录 引言 建堆的两种方法 一、向上调整建堆 二、向下调整建堆 两种建堆方式的性能比较 堆排序 堆排序的思想 堆排序的时间复杂度 堆排序的空间复杂度 堆排序代码 引言 首先&#xff0c;介绍一下本次的主人公——堆。堆是一种数据结构&#xff0c;在逻辑上是一棵二叉…

Java数据结构-优先级队列

文章目录 前言一、优先级队列1.1 概念 二、优先级队列的模拟实现2.1 堆的概念2.2 堆的存储方式2.3 堆的创建2.3.1 堆向下调整2.3.2 堆的创建2.3.3 建堆的时间复杂度 2.4 堆的插入与删除2.4.1 堆的插入2.4.2 堆的删除 2.5 用堆模拟实现优先级队列 三、常用接口介绍3.1 PriorityQ…

php 对接IronSource海外广告平台收益接口Reporting API

今天对接的是IronSource广告reporting api接口&#xff0c;拉取广告收益回来自己做统计。记录分享给大家 首先是文档地址,进入到Inmobi后台就能看到文档地址以及参数&#xff1a; 文档地址&#xff1a;https://developers.is.com/ironsource-mobile/air/reporting/ 在这里插入图…

5 个适用于 Windows 10 和 11 的最佳 PDF 转 Word 转换器

PDF 文件是共享文档的首选格式&#xff0c;但是此类文件存在一些限制&#xff0c;导致难以修改或编辑。因此&#xff0c;您可能会发现自己正在寻找一种将 PDF 文件转换为 Word 或其他可编辑格式的方法。 有许多不同的 PDF 转换器&#xff0c;每种转换器提供的功能略有不同。本…

代码+视频,R语言使用BOOT重抽样获取cox回归方程C-index(C指数)可信区间

bootstrap自采样目前广泛应用与统计学中&#xff0c;其原理很简单就是通过自身原始数据抽取一定量的样本&#xff08;也就是取子集&#xff09;&#xff0c;通过对抽取的样本进行统计学分析&#xff0c;然后继续重新抽取样本进行分析&#xff0c;不断的重复这一过程N&#xff0…

还原wps纯粹的编辑功能

1.关闭稻壳模板&#xff1a; 1.1. 启动wps(注意不要乱击稻壳模板&#xff0c;点了就找不到右键菜单了) 1.2. 在稻壳模板选项卡右击&#xff1a;选不再默认展示 2.关闭托盘中wps云盘图标&#xff1a;右击云盘图标/同步与设置&#xff1a; 2.1.关闭云文档同步 2.2.窗口选桌面应用…

BFS 最短路径

目录 原理剖析&#xff1a; 1、 1926. 迷宫中离入口最近的出口 2、 433. 最小基因变化 3、 127. 单词接龙 4、 675. 为高尔夫比赛砍树 原理剖析&#xff1a; 为什么BFS能够解决最短路径问题&#xff1f; 对于无权图(边权为1)或所有边权重相等的情况&#xff0c;BFS是一种有…

ASP.NET Mvc+FFmpeg+Video实现视频转码

目录 首先&#xff0c;做了视频上传的页面&#xff1a; FFmpeg&#xff1a;视频转码 FFmpegHelper工作类&#xff1a; 后台控制器代码&#xff1a; 前端视图代码&#xff1a; 参考文章&#xff1a; 首先&#xff0c;做了视频上传的页面&#xff1a; 借鉴了这篇文章 ASP.…

【pycharm】如何将pacharm设置成中文

【pycharm】汉化教程——如何将pacharm设置成中文 1、打开pycharm 2、点击file 3、点击setting——Plugins——搜索Chinese——点击如下图图标进行下载 汉化后界面情况&#xff1a;

【数据结构与算法】(13):交换排序之冒泡排序和快速排序

&#x1f921;博客主页&#xff1a;Code_文晓 &#x1f970;本文专栏&#xff1a;数据结构与算法 &#x1f63b;欢迎关注&#xff1a;感谢大家的点赞评论关注&#xff0c;祝您学有所成&#xff01; ✨✨&#x1f49c;&#x1f49b;想要学习更多数据结构与算法点击专栏链接查看&…

生成器模式(软考uml C++版)

按照软考中级软件设计师中指定的生成器模式uml图&#xff0c;可编写对应的C&#xff0b;&#xff0b;代码&#xff1a; #include<iostream> #include<vector> #include<string> using namespace std;/*创建者模式&#xff0c;又名生成器模式意图&#xff1a…

每日五道java面试题之springMVC篇(四)

目录&#xff1a; 第一题. Spring MVC怎么样设定重定向和转发的&#xff1f;第二题.Spring MVC怎么和AJAX相互调用的&#xff1f;第三题. 如何解决POST请求中文乱码问题&#xff0c;GET的又如何处理呢&#xff1f;第四题. Spring MVC的异常处理&#xff1f;第五题. 如果在拦截请…

【JWT】入门 *JWT*,并封装一个实用的 *JWT* 工具类

个人简介&#xff1a;Java领域新星创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 【JWT】入门 *JWT*&#xff0c;并封装一个实用…

SQLiteC/C++接口详细介绍之sqlite3类(八)

返回目录&#xff1a;SQLite—免费开源数据库系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;七&#xff09; 下一篇&#xff1a; SQLiteC/C接口详细介绍之sqlite3类&#xff08;八&#xff09;&#xff08;暂未发表&#xff09; 24.sqlite3_cr…