C#学习日记

发布于:2025-07-05 ⋅ 阅读:(18) ⋅ 点赞:(0)

Dictionary

知识点一 Dictionary的本质


            可以将Dictionary理解为 拥有泛型的Hashtable
            它也是基于键的哈希代码组织起来的 键/值对
            键值对类型从Hashtable的object变为了可以自己制定的泛型
 

知识点二 申明
           

需要引用命名空间 using System.Collections.Generic
            Dictionary<int, string> dictionary = new Dictionary<int, string>();
 

知识点三 增删查改


            注意:不能出现相同键
            dictionary.Add(1, "123"); dictionary(键,"值");
            dictionary.Add(2, "222");
            dictionary.Add(3, "222");
            //dictionary.Add(3, "123");
 


            1.只能通过键去删除
              删除不存在键 没反应

            dictionary.Remove(1);
            dictionary.Remove(4);

            2.清空
            dictionary.Clear();
            dictionary.Add(1, "123");
            dictionary.Add(2, "222");
            dictionary.Add(3, "222");
 

 查
            1.通过键查看值
              找不到直接报错
            Console.WriteLine(dictionary[2]);
            Console.WriteLine(dictionary[4]);
            Console.WriteLine(dictionary[1]);

            2.查看是否存在
              根据键检测

            if( dictionary.ContainsKey(4) )
            {
                Console.WriteLine("存在键为1的键值对");
            }
              根据值检测
            if (dictionary.ContainsValue("1234"))
            {
                Console.WriteLine("存在值为123的键值对");
            }


            Console.WriteLine(dictionary[1]);
            dictionary[1] = "555";
            Console.WriteLine(dictionary[1]);
 

知识点四 遍历
            Console.WriteLine("**************");
            Console.WriteLine(dictionary.Count);
            1.遍历所有键
            foreach (int item in dictionary.Keys)
            {
                Console.WriteLine(item);
                Console.WriteLine(dictionary[item]);
            }
            2.遍历所有值
            Console.WriteLine("**************");
            foreach (string item in dictionary.Values)
            {
                Console.WriteLine(item);
            }
            3.键值对一起遍历
            Console.WriteLine("**************");
            foreach (KeyValuePair<int,string> item in dictionary)
            {
                Console.WriteLine("键:" + item.Key + "值:" + item.Value);
            } 



顺序存储和链式存储

 请说出常用的数据结构有哪些?

 答:数组、栈、队列、链表、树、图、堆、散列表
 

请描述顺序存储和链式存储的区别?

  顺序存储:内存中用一组地址连续的存储单元存储线性表(地址存储)
  链式存储:内存中用一组任意的存储单元存储线性表(任意地方)

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

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

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

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

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

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

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

            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;
            }
        }
    }

知识点五 自己实现一个最简单的单向链表
    /// <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;
                }
            }
        }
    }


 

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

 

Linkedlist

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

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

知识点三 增删查改

   增
           

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

           

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

           

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

4.在某一个节点之前添加一个节点
              要指定节点 先得得到一个节点
            linkedList.AddBefore(n, 11);
 


           

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);
 


           

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");
            }
 


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

   

知识点四 遍历
           

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;
            }


Next:从前往后

Previous:从后往前

泛型栈和队列

知识点一 回顾数据容器

           变量
           

无符号
            byte ushort uint ulong
           

有符号
            sbyte short int long
           

浮点数
            float double decimal
           

特殊
            char bool string
 

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

 

数据集合
            using System.Collections;

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

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

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

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

 

委托

事件

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

知识点二 事件的使用
    申明语法:
    访问修饰符 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");
        }
    }
 

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

 

    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.事件 不能作为 函数中的临时变量的。 委托可以


 

匿名函数

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

知识点二

基本语法
     delegate (参数列表)
     {
        函数逻辑
     };


            何时使用?
            1.函数中传递委托参数时
            2.委托或事件赋值时

 

知识点三 使用
           

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

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

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

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

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

        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表达式

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

知识点二 lambad表达式语法
       

    匿名函数
            delegate (参数列表)
            {

            };

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


 

知识点三 使用
           

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("有返回值有参数的lambad表达式{0}", value);
                return 1;
            };
            Console.WriteLine(a4("123123"));

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

 

            Test t = new Test();

            t.DoSomthing();
        }
    }

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

    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();
        }
    }


    
    总结

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

    缺点无法指定移除

 

 


网站公告

今日签到

点亮在社区的每一天
去签到