07-C#

手把手教你用C#实现DL/T645-07电表数据采集(附完整代码) 本文详细介绍了如何使用C#实现DL/T645-07协议的电表数据采集。内容涵盖协议帧结构解析、C#核心代码实现、基于Socket的稳定通讯架构设计、设备抽象模型以及生产级的数据存储与异常处理机制,为构建工业级能源管理系统提供完整实战指南。 阅读详情

C#.Net-数据结构-学习笔记

一、数据结构概述

数据结构是底层数据的存储方式,分四大类:

  • Set集合:纯粹的容器,无序存储,元素唯一
  • 线性结构:一对一存储,如数组、链表、队列、栈
  • 树形结构:一对多存储,如二叉树、表达式目录树、菜单结构
  • 图状结构:多对多存储,如拓扑图、地图网络

二、线性结构

连续存储(数组类)

Array

内存连续分配,元素类型相同,长度固定。支持索引访问,读取快;增删需要移动元素,慢。

int[] intArray = new int[3];
intArray[0] = 123;
string[] stringArray = new string[] { "123", "234" };
ArrayList

长度可动态增加,元素类型为 object,值类型存入时会装箱,取出时需拆箱和强转。非泛型,性能低,现代项目基本不用。

ArrayList arrayList = new ArrayList();
arrayList.Add("Richard");
arrayList.Add(32); // 值类型装箱
var value = (int)arrayList[2]; // 拆箱 + 强转
List<T>

底层也是数组,但泛型、类型安全,避免装箱拆箱,性能高于 ArrayList。读取快,增删慢。

List<int> intList = new List<int>() { 1, 2, 3, 4 };
intList.Add(123);
int val = intList[0]; // 直接访问,无需转换

性能排序:Array ≈ List<T> > ArrayList


非连续存储(链表类)

LinkedList<T>

双向链表,每个节点记录前后节点地址,内存非连续。不支持索引访问,查找只能遍历(慢);增删只需修改指针(快)。

LinkedList<int> linkedList = new LinkedList<int>();
linkedList.AddFirst(123);
linkedList.AddLast(456);
LinkedListNode<int> node = linkedList.Find(123);
linkedList.AddBefore(node, 9);
linkedList.AddAfter(node, 9);
linkedList.Remove(node);
Queue<T>

先进先出(FIFO),像没有瓶底的瓶子。

Queue<string> queue = new Queue<string>();
queue.Enqueue("one");       // 入队
string item = queue.Dequeue(); // 出队并移除
string peek = queue.Peek();    // 查看队首,不移除

应用场景:任务队列、消息队列、日志异步处理。

Stack<T>

先进后出(LIFO),像有瓶底的瓶子。

Stack<string> stack = new Stack<string>();
stack.Push("one");         // 入栈
string item = stack.Pop(); // 出栈并移除
string peek = stack.Peek(); // 查看栈顶,不移除

应用场景:表达式求值、撤销操作、解析表达式目录树。


三、Set 集合(去重容器)

HashSet<T>

基于哈希分布,自动去重,无序存储。对于引用类型,默认按引用判断是否重复;若要按值去重,需重写 Equals 和 GetHashCode。

HashSet<string> hashSet = new HashSet<string>();
hashSet.Add("123");
hashSet.Add("123"); // 重复,不会添加
Console.WriteLine(hashSet.Count); // 1

// 集合运算
HashSet<string> set1 = new HashSet<string>() { "A", "B", "C" };
HashSet<string> set2 = new HashSet<string>() { "B", "C", "D" };
set1.UnionWith(set2);           // 并集
set1.IntersectWith(set2);       // 交集
set1.ExceptWith(set2);          // 差集
set1.SymmetricExceptWith(set2); // 对称差(补集)

应用场景:点赞去重、IP统计、好友推荐(求差集找出"对方认识但我不认识的人")。

SortedSet<T>

自动去重 + 自动排序。可通过 IComparer<T> 自定义排序规则。

SortedSet<string> sortedSet = new SortedSet<string>();
sortedSet.Add("689");
sortedSet.Add("123");
sortedSet.Add("456");
// 遍历输出:123, 456, 689

应用场景:实时排行榜。


四、键值对结构(Key-Value)

哈希散列的核心思想:用 key 计算哈希值,映射到数组索引,实现 O(1) 的增删查改。代价是用空间换性能,数据量过大时散列冲突增多,性能下降。

Hashtable

非泛型,key 和 value 均为 object,有装箱拆箱开销。支持线程安全包装。

Hashtable table = new Hashtable();
table.Add("key1", "value1");
table["key2"] = "value2"; // 直接赋值,key不存在则新增,存在则覆盖
Hashtable.Synchronized(table); // 线程安全版本(单写多读)
Dictionary<TKey, TValue>

泛型版哈希表,类型安全,性能高。按插入顺序遍历(但官方不保证顺序,不应依赖此特性)。非线程安全,多线程场景用 ConcurrentDictionary<TKey, TValue>。

Dictionary<int, string> dic = new Dictionary<int, string>();
dic.Add(1, "HaHa");
dic[4] = "HuHu"; // 不存在则新增,存在则覆盖
// dic.Add(4, "HuHu"); // key已存在会抛异常
foreach (var item in dic)
{
    Console.WriteLine($"Key:{item.Key}, Value:{item.Value}");
}
SortedDictionary<TKey, TValue>

自动按 Key 排序,性能略低于 Dictionary。

SortedDictionary<int, string> dic = new SortedDictionary<int, string>();
dic.Add(5, "HoHo");
dic.Add(1, "HaHa");
dic.Add(3, "HeHe");
// 遍历输出顺序:1, 3, 5
SortedList

非泛型,按 Key 排序,支持索引访问,内存占用比 SortedDictionary 小。TrimToSize() 可最小化内存开销。

SortedList sortedList = new SortedList();
sortedList.Add("First", "Hello");
sortedList["Third"] = "~~";
sortedList.TrimToSize(); // 释放多余内存

五、接口体系

  • IEnumerable:所有集合都实现了此接口,提供统一的遍历方式(foreach),核心方法是 GetEnumerator(),返回迭代器
  • ICollection<T>:继承自 IEnumerable<T>,增加了 Count、CopyTo、Contains 等方法,以及 Add、Remove(泛型版本才有增删)
  • IList<T>:继承自 ICollection<T>,增加了索引访问(this[int index])和 Insert、RemoveAt
  • IQueryable:用于延迟查询(LINQ、EF),基于表达式目录树,遍历时才真正执行查询

六、迭代器模式

结论:迭代器模式为不同数据结构提供统一的访问接口,客户端无需关心底层是数组还是链表。

C# 中所有集合都实现了 IEnumerable,foreach 本质上就是调用 GetEnumerator() 获取迭代器,再反复调用 MoveNext() 和 Current。

自定义迭代器

有两种方式实现自定义迭代器:

方式一:用 yield(推荐,简洁)

public class EnumeratorIterator<TSource> : IEnumerable<TSource>
{
    private readonly IEnumerable<TSource> source;
    private readonly Func<TSource, bool> predicate;

    public EnumeratorIterator(IEnumerable<TSource> source, Func<TSource, bool> predicate)
    {
        this.source = source;
        this.predicate = predicate;
    }

    public IEnumerator<TSource> GetEnumerator()
    {
        foreach (var item in source)
        {
            if (predicate(item))
                yield return item; // 编译器生成状态机
        }
    }

    IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
}

方式二:手动实现 IEnumerator(理解底层用)

public class ManualIterator<T> : IEnumerator<T>
{
    private readonly T[] _data;
    private int _index = -1;

    public ManualIterator(T[] data) { _data = data; }

    public T Current => _data[_index];
    object IEnumerator.Current => Current;

    public bool MoveNext()
    {
        _index++;
        return _index < _data.Length;
    }

    public void Reset() { _index = -1; }
    public void Dispose() { }
}

方式二展示了 yield 背后编译器实际生成的逻辑:维护一个 _index 状态,每次 MoveNext() 推进一步,Current 返回当前值。

迭代器模式实战:统一访问不同菜单

KFC 菜单用数组存储,麦当劳菜单用 List 存储,通过统一的 IIterator<Food> 接口访问:

IIterator<Food> iterator = kfcMenu.GetEnumerator();
while (iterator.MoveNext())
{
    Food food = iterator.Current;
    Console.WriteLine(food.Name);
}
// 换成麦当劳菜单,调用方式完全一样
IIterator<Food> iterator1 = macDonaldMenu.GetEnumerator();
while (iterator1.MoveNext()) { ... }

七、yield 关键字

结论:yield 是语法糖,编译器自动生成状态机(迭代器),实现 MoveNext、Current、Reset,让按需返回数据变得极其简洁。

yield return:按需返回

public IEnumerable<int> Power()
{
    for (int i = 0; i < 10; i++)
    {
        yield return Get(i); // 每次调用 MoveNext 才执行到这里
        Console.WriteLine("yield 之后继续执行");
    }
}

调用 Power() 时不会立即执行任何代码,只有 foreach 遍历时,每次 MoveNext() 才推进一步。

yield break:提前终止

public IEnumerable<int> CreateEnumerable()
{
    for (int i = 0; i < 5; i++)
    {
        yield return i;
        if (i == 4)
            yield break; // 终止迭代,后续代码不再执行
    }
    yield return -1; // 不会执行到
}

yield 与 finally

含有 yield 的方法中,finally 块会在迭代器被释放(Dispose)时执行,即使中途 break 也会触发:

public IEnumerable<int> CreateEnumerable()
{
    try
    {
        for (int i = 0; i < 5; i++)
        {
            yield return i;
        }
    }
    finally
    {
        Console.WriteLine("停止迭代!"); // foreach break 后也会执行
    }
}

yield vs 普通方法

// yield:按需获取,延迟执行,节省内存
public IEnumerable<int> Power()
{
    for (int i = 0; i < 10; i++)
        yield return Get(i); // 要一个拿一个
}

// 普通方法:一次性全部计算,全部放入内存
public IEnumerable<int> Common()
{
    List<int> list = new List<int>();
    for (int i = 0; i < 10; i++)
        list.Add(Get(i)); // 先全部算完
    return list;
}

自定义 LINQ 扩展方法

public static IEnumerable<T> ElevenWhere<T>(
    this IEnumerable<T> source,
    Func<T, bool> func)
{
    foreach (var item in source)
    {
        if (func.Invoke(item))
            yield return item; // 延迟执行,遍历时才过滤
    }
}

// 使用
var result = studentList.ElevenWhere(s => s.Age < 30);
foreach (var item in result) // 遍历时才真正执行过滤逻辑
{
    Console.WriteLine(item.Name);
}

yield 的应用场景:大数据集分页加载、无限序列生成、LINQ 的 Where/Select 等延迟查询。


八、dynamic 关键字

结论:dynamic 是 C# 4.0 引入的动态类型,让 C# 具备弱类型特点,类型检查推迟到运行时。

// 强类型:编译时检查
string s = "abcd";
// int i = (int)s; // 编译错误

// dynamic:运行时检查
dynamic d = "abcd";
int i = (int)d;  // 编译通过,运行时报错
d.Hello();       // 编译通过,运行时报错

任何与 dynamic 交互的表达式,结果也是 dynamic:

dynamic str = "abcd";
Console.WriteLine(str.Length);      // dynamic
Console.WriteLine(str.Substring(1)); // dynamic

dynamic 的三个主要用途:

  1. 代替反射,性能比反射高:
object obj = new YieldDemo();

// 反射方式
Type type = obj.GetType();
type.GetMethod("Power").Invoke(obj, null);

// dynamic 方式(更简洁,性能更好)
dynamic dObj = obj;
dObj.Power();
  1. 简化数据绑定,无需强转
  2. 与 COM/C++ 互操作更方便

注意:dynamic 失去了编译时类型检查,错误只在运行时暴露,使用时需谨慎。


九、线程安全集合

System.Collections.Concurrent 命名空间提供线程安全版本:

  • ConcurrentQueue<T>:线程安全的队列(FIFO)
  • ConcurrentStack<T>:线程安全的栈(LIFO)
  • ConcurrentBag<T>:线程安全的无序集合
  • ConcurrentDictionary<TKey, TValue>:线程安全的字典
  • BlockingCollection<T>:支持阻塞和限界的集合

十、性能对比与选型

数据结构查询增加删除特点
ArrayO(1)O(n)O(n)固定长度,内存连续
List<T>O(1)O(1)/O(n)*O(n)动态长度,内存连续
LinkedList<T>O(n)O(1)O(1)链表,非连续
HashSet<T>O(1)O(1)O(1)去重,无序
Dictionary<K,V>O(1)O(1)O(1)键值对,哈希散列

*List 尾部追加不需扩容时 O(1),触发扩容或中间插入时 O(n)

选型建议:

  • 频繁随机访问、少量增删 → Array 或 List<T>
  • 频繁头部/中间增删 → LinkedList<T>
  • 需要去重 → HashSet<T>
  • 需要键值映射,增删查改都快 → Dictionary<TKey, TValue>
  • 需要自动排序 → SortedSet<T> 或 SortedDictionary<TKey, TValue>
  • 多线程场景 → Concurrent 系列
C# WinForm —— 07 Form窗体介绍 下面这个是文件名,当修改文件名时,窗体的 (Name)属性的值 和 代码页面的类名 都会随之修改。InitializeComponent()函数是窗体的初始化函数,里面主要定义了一些窗体和控件的属性,触发的事件/方法等,是 根据用户在属性里设置的内容 自动生成的当 窗体 在文件夹下面的时候,调用时,需要用 “文件夹名字.窗体” 阅读详情

相关推荐

从FreeCAD到BIMBase:PlaneGCS约束求解器的移植使用指南

本文为BIMBase平台的开发者提供了一份详细的PlaneGCS几何约束求解器移植与集成指南。文章深入探讨了如何从FreeCAD中提取PlaneGCS核心代码,并将其适配到BIMBase的C++二次开发环境中,重点解决了内存管理、数据模型桥接、约束抽象以及性能调优等关键问题,旨在帮助开发者构建稳定高效的参数化设计模块。

weixin_29213525的博客 320

C#迭代器——由foreach说开去

C#迭代器——由foreach说开去 IEnumerable和IEnmerator yield retun StartCoroutine

O213的博客 7366

手机流畅的秘密:拆解LPDDR5X在骁龙8 Gen2平台上的电源与信号完整性设计

本文深入解析了LPDDR5X内存与骁龙8 Gen2处理器在旗舰手机中的协同设计,重点探讨了0.5V超低电压下的电源完整性设计以及8.5Gbps高速传输中的信号完整性挑战。通过详细的电源树架构、关键器件选型和布线策略分析,揭示了如何实现高性能与低功耗的完美平衡,为手机流畅体验提供硬件保障。

weixin_30235225的博客 483

C#笔记(枚举器和迭代器)

1 枚举器 为什么数组可以使用 foreach 输出各元素? 这是因为数组是 枚举类型(enumerable) ,它通过 GetEnumerator 方法提供一个 枚举器(enumerator),即能实现 GetEnumerator 方法的类型是 枚举类型 枚举器可以依次返回请求的数组的元素,枚举器知道 项的次序 并跟踪它的序列位置,然后返回 请求的当前项 对象枚举类型获取枚举器的方法是调用...

KeePCoding 1403

Xilinx Vitis arm-xilinx-eabi-gcc.exe: error: *.c: Invalid argument

This problem may be a bugof version 2021.1 only. Unfortunately, I'm just currently using this version. After Build Project: Find where this Makefile is through "make: Leaving directory" information. For me, it is located atZynq_CPU_wrapper_hw_platfo...

Ayka的博客 6660

从东南大学906被刷到调剂过程(内含个人感想)

介绍了自己在看到初试成绩,到后期复习以及调剂的全过程,当中也包含了自己的复试中的感想,希望大家理性看待。

Philo的博客 2854

C# 迭代器(iterators)

An iterator can be used to step through collections such as lists and arrays. 迭代器“逐步地”访问集合,比如说列表和数组。“逐步地”指的是迭代器每次只返回集合中的一个元素,而不是全部返回。 迭代器块是有一个或多个yield语句的代码块,以下3种类型的代码块中的任意一种都可以是迭代器块: 1. 方法主体 ...

junex的博客 3336

企业AI Agent审计与合规:从日志留存到网络联动的完整落地指南

审计日志是企业安全与合规体系的基石,尤其在AI Agent大规模接入业务后,如何完整记录其行为链成为新的挑战。从日志留存180天到PCI DSS合规要求,审计体系需要覆盖会话、推理、工具调用、数据访问等关键环节,并结合不可篡改的存储设计,确保证据链可信。同时,借鉴上网行为管理设备对应用行为控制审计的思路,以及Kismet被动监测网络流量的方法论,将网络侧审计与应用侧日志交叉验证,能有效发现越权和异常行为。本文从日志采集、全链路追踪、基线预警到多租户隔离,系统梳理了AI Agent审计与合规落地的关键路径,帮

weixin_32234493的博客 263

C# DLT645 97/07数据采集工具

C# DLT645 97/07数据采集工具

arno1988的专栏 1158

《Kubernetes部署篇:基于x86_64+aarch64架构CPU+containerd一键离线部署容器版K8S1.33.3高可用集群》

《Kubernetes部署篇:基于x86_64+aarch64架构CPU+containerd一键离线部署容器版K8S1.33.3高可用集群》

东城绝神 1367

阿里云百炼平台创建智能体-上传文档

阿里云百炼智能体-上传文档文件

weixin_43930851的博客 1354

C# winform 大数据计算 得出 双色球 :01 07 20 29 30 32 07必中,但不知道是哪期。

C# winform 大数据计算 得出 双色球 :01 07 20 29 30 32 07必中,但不知道是哪期。双色球 :01 07 20 29 30 32 07。

曾经的我也很优秀,现在的我一无所有。 533

RK3568平台(内存篇)DDR定频修改

如果需要DDR定频,则还需要注释掉其他多余的DDR频率,保留一个即可,例如注释掉opp-528000000、opp-1068000000、opp-1560000000,保留opp-2750000000则是定频最大频率。如果回退必须将 dmc_opp_table节点 的其他频率打开,否则系统无法选择到其他频率运行。ddr频点不对,先把ddr变频关掉,关闭后系统就不会一直重启了,以下是修改DDR频点的。rk3588修改定频节点在rk3588s.dtsi 的 dmc: dmc。

weixin_49303682的博客 1620

C#基础07

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Base07 { class Program { static void Main(string[] args) ...

qq_43724709的博客 345

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

C#笔记07 枚举

C#笔记07 枚举 ——本系列是基于人民邮电出版社《C#2008 C#图解教程》、清华大学出版社《C#入门经典(第五版)》两本书的自学C#笔记,如果您发现了本文的纰漏,还望不吝指正。 写在前边 *复习:类型是一种模版,C#提供了15种预定义类型,还可以创建自己的用户定义类型,有6种类型: 类类型(class) 结构类型(struct) 数组类型(array) 枚举类型(enum) 委托类型(delegate) 接口类型(interface) 1. 枚举类型的声明 枚举是值类型,直接存储它们的数据 枚举

Tervor_HAN的博客 271

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

C#笔记07 继承

多态、虚方法、抽象。

记录学习痕迹的公众号:Piper蛋窝 516

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

07C#对象

C#对象 转载于:https://www.cnblogs.com/Aha-Best/p/10931592.html

weixin_30783913的博客 66

C#语言基础速成Day07

昨天我们学习了C#属性访问器、方法参数和C#字符串,StringBuilder的一些内容,今天我们学习C#常见数据结构。C# 提供了多种数据结构来帮助开发者高效管理和处理数据。通过了解和正确使用这些数据结构,可以大大提高代码的性能和可维护性。根据具体的需求选择合适的集合类型、栈、队列或字典,是编写高质量代码的关键。呜呼!今天我们C#语言的基础部分就算结束了,后面还有更加深奥的知识等着我们。以后我们还会更新关于其他语言的一些知识。C#更加复杂的部分还在后面我们今后可以通过一些实战的案例来学习。

2301_79175212的博客 1971

C#学习教程07——接口

C#学习教程,适合新手小白,附带学习源码。

Kint的博客 2142

C#日志模块 07

C#日志模块 日志模块是生产过程中记录,软件运行信息的重要文件,包含了软件的运行日志,警告日志,和错误日志。方便事后程序员排查问题 首先要确定需要的参数,ID是必须的,其次日志得含有日期,标题,内容,日志类型。查询日志一般来说是通过开始时间,结束时间,日志类型来查询。 model层建立LogsInfo public class LogsInfo { public int LId { get; set; } public string LDataTime { get;

Abril_的博客 1013

C# 图片压缩2021-07-07

C# 图片压缩 转自:https://www.skyfinder.cc/2020/04/04/csharpimagecompress/ 添加NuGet包 Main 方法调用 CompressionImage()即可,参数1:需要压缩的图片路径,参数2:压缩完保存的目录 保存的图片与原图片名称相同 using System; using System.Drawing; using System.Drawing.Imaging; using System.IO; namespace ConsoleApp1

Nonsense8d的博客 644

C# 2020/07/03

学习C#的第九天 接口的实现与继承 - 实现过程必须在实现接口的类中完成 - 接口可以多次继承 namespace winApp { interface Interface3 : Interface1, Interface2 { } } - 不同的接口(不包含【派生)中允许有同名的成员 - 同一接口中成员,即使类型不同,也不能重名 namespace winApp { interface Interface3 : Interface1, Interfac

Fazerious的博客 250

C#笔记进阶篇07 事件

C#笔记进阶篇07 事件 ——本系列是基于人民邮电出版社《C#2008 C#图解教程》、清华大学出版社《C#入门经典(第五版)》两本书的自学C#笔记,如果您发现了本文的纰漏,还望不吝指正。 写在前边 ...

Tervor_HAN的博客 361

C#笔记-07

枚举(值类型) using System; namespace day7 { [Flags]enum PersonStyle { tall = 1, rich = 2, handsome = 4, man = 8, woman = 16 } class Program { static void Main(string[] args) {

qq_37609345的博客 151
上一篇: 11-C#
下一篇: 06-C#
似水明俊德
博客等级 码龄8年 247粉丝 276原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值