C#学习日记

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;

}

}

}

cs 复制代码
知识点五 自己实现一个最简单的单向链表
    /// <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.事件 不能作为 函数中的临时变量的。 委托可以

匿名函数

知识点一 什么是匿名函数

顾名思义,就是没有名字的函数

匿名函数的使用主要是配合委托和事件进行使用

脱离委托和事件 是不会使用匿名函数的

知识点二

cs 复制代码
基本语法
     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表达式语法

cs 复制代码
    匿名函数
            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表达式

缺点无法指定移除

相关推荐
凛铄linshuo6 分钟前
爬虫简单实操2——以贴吧为例爬取“某吧”前10页的网页代码
爬虫·python·学习
charlie11451419120 分钟前
深入理解Qt的SetWindowsFlags函数
开发语言·c++·qt·原理分析
大春儿的试验田1 小时前
高并发收藏功能设计:Redis异步同步与定时补偿机制详解
java·数据库·redis·学习·缓存
likeGhee1 小时前
python缓存装饰器实现方案
开发语言·python·缓存
whoarethenext1 小时前
使用 C++/Faiss 加速海量 MFCC 特征的相似性搜索
开发语言·c++·faiss
项目題供诗1 小时前
黑马python(二十五)
开发语言·python
慌糖1 小时前
RabbitMQ:消息队列的轻量级王者
开发语言·javascript·ecmascript
金色光环2 小时前
【Modbus学习笔记】stm32实现Modbus
笔记·stm32·学习
醇醛酸醚酮酯2 小时前
Qt项目锻炼——TODO清单(二)
开发语言·数据库·qt
jioulongzi2 小时前
记录一次莫名奇妙的跨域502(badgateway)错误
开发语言·python