HashTable在WinForm下的上位机软件开发中的应用

2021-09-16 08:00宋城虎
科技创新与应用 2021年25期
关键词:双键键值哈希

宋城虎,马 静

(中国电子科技集团公司第二十七研究所,河南 郑州450047)

HashTable(也叫哈希表),是根据关键码值(key,value)而直接进行访问的数据结构。它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。在.NET Framework中,System.Collections命名空间提供了HashTable容器的实现。HashTable中key、value键值对均为object类型,所以HashTable可以支持任何类型的key、value键值对。

在工程应用中,上位机应用程序通常需要与多个下位机或者外围设备进行通信,从而实现对设备的控制以及设备状态与数据的接收与处理。当上位机通信的对象增多时,上位机编程也会随之变得越来越复杂。

本文以C#WinForm框架下的上位机编程为例讨论一种HashTable的应用方法,该方法实现简单,使用灵活方便,可大幅度简化上位机程序开发中的一些复杂问题。

1 双键哈希表

HashTable的结构为1个key对应1个value,这个结构在实际应用中经常显得过于单一,如果两个key对应1个value那么显然就会灵活许多,我们就可以构建类似于“对象‘key1’的‘key2’属性为‘value’”这样的逻辑关系(如图1、图2所示)。

由于在.NET Framework中的HashTable中key、value键值对均为object类型,那么value本身也可以存储另一个HashTable,因此我们可以构建两层哈希表来实现双键哈希结构,即在第一层HashTable1中通过key1存储第二层HashTable2,在第二层HashTable2中通过key2存储value值(如图3所示)。

于是我们只需要定义哈希表的三个基本操作“存储、读取、删除”即可实现双键哈希表功能。其中删除需要针对key1和key2定义两个函数,因此总共需要定义4个函数,以下给出代码:

1.1 存储函数

void hash_save(Hashtable hash,object key1,object key2,object value)

{

if(hash!=null)

{

Hashtable hash1;

if(hash[key1]==null)

{

hash1=new Hashtable();

hash.Add(key1,hash1);

}

else hash1=(Hashtable)(hash[key1]);

if(hash1[key2]==null)hash1.Add(key2,value);

else{

hash1.Remove(key2);

hash1.Add(key2,value);

}}}

1.2 读取函数

object hash_Load(Hashtable hash,object key1,object key2)

{

i(fhash[key1]!=null)return((Hashtable)(hash[key1]))[key2];

else return null;

}

1.3 清除key1

void hash_remove_key1(Hashtable hash,object key1)

{

((Hashtable)(hash[key1])).Clea(r);

}

1.4 清除key2

void hash_remove_key2(Hashtable hash,object key1,object key2)

{

((Hashtable)(hash[key1])).Remove(key2);

}

2 工程应用

2.1 通信连接管理

上位机应用程序通常需要与多个下位机或者外围设备进行通信,这里以udp通信为例(其他通信原理相同)。WinForm框架中有Socket类用来实现网络通信,在建立一个udp通信时,我们需要实例化一个Socket对象,定义目标IPEndPoint和本地IPEndPoint,建立监听线程,在监听线程的回调函数中编写数据处理代码。

当上位机要与多个对象同时建立通信时,通常将以上过程复制多次,并用不同的变量名进行区分。当通信对象特别多时代码的编辑和维护就会变得非常困难,且不方便移植也不利于复用。

这里借助上文定义的双键哈希表可极大程度地优化这一过程。实现思路如下:

(1)给每一条通信链路定义一个唯一标识Sign;

(2)以标识Sign为key1、以参数标识为key2对该链路的所有相关参数进行注册,存储在哈希表中,例如给名为“sign1”的连接注册目标IP:

hash_sav(ehash1,"sign1","目标IP","192.168.1.10");

(3)定义一个主索引用来记录所有已经注册的Sign,主索引依然可以使用双键哈希表实现,例如索引中增加一个新的名为“Sign1”的连接:

int max=(int)hash_load(hash1,"udp主索引","连接总数")+1

hash_save(hash1,"udp主索引","连接总数",max);

hash_save(hash1,"udp主索引",max,"Sign1");

(4)定义udpcreate函数,该函数针对一个特定Sign,建立一条udp通信链路,返回初始化完毕的Socket对象。所有初始化相关参数以Sign为key在哈希表中读取,例如读取连接“sign1”的目标IP:

string ip=hash_load(hash1,"sign1","目标IP").ToString();

(5)在程序的udp初始化环节遍历第3步中定义的主索引,调用udpcreate函数初始化所有udp连接;

(6)单独定义每条通信的接收数据的回调函数,回调函数可以以委托结合文本宏的方式注册在哈希表中。通信链路的注册可以通过文件读取转移到配置文件中。

主索引操作以及udpcreate函数等通用型代码均可封装到一个模块中,方便移植和复用。这样每次开发一个新的上位机程序只需要编写数据处理的回调函数以及根据工程需求编辑配置文件即可。

2.2 其他功能应用

使用双键哈希表可以不以变量为载体,动态地存储和读取任意数据,并能将数据关联在任意对象上,这意味着它几乎可以应用到程序的任何地方。例如,我们可以在控件刷新时读取控件上绑定的状态数据来决定控件的外观或者显示文字,这样我们就可以通过改变哈希值来控制控件的状态刷新;我们还可以利用双键哈希表将两个控件关联起来,实现类似于父节点和子节点这样的结构关系等。

2.3 注意事项

由于使用双键哈希表会自动创建许多次级HashTable,当某个key1不再使用时,应当注意释放key1对应的资源,也就是调用上文提到的hash_remove_key1函数,避免出现内存泄漏。

3 实际应用

在某工程项目的上位机软件开发中,与上位机通信的分机有7个,其中包含udp和串口通信。该软件开发中大量使用了双键哈希表,与以往的开发经验做对比极大程度地提高了开发效率,调试过程中的bug也有显著减少,并且能够简单快捷地移植到其他项目开发中(如图4、图5所示)。

图4 某项目上位机软件通信调试界面

图5 某项目通信配置文件

4 结束语

文章利用.NET Framework中的HashTable的key和value可支持任意类型值的特点,设计了一种双键哈希表,可实现将任意类型的两个对象作为索引存储一个任意类型的value值。文章以WinForm框架下上位机程序开发为背景,以udp通信编程为例,详细阐述了该技术的应用方法与效果,对该技术在其他方面的应用进行了展望。该技术已在作者参与的多个工程项目中得到应用,并得到了非常好的应用效果。

猜你喜欢
双键键值哈希
有机合成推断题的梗点知识举例
复杂断块调剖提效研究
基于特征选择的局部敏感哈希位选择算法
哈希值处理 功能全面更易用
文件哈希值处理一条龙
非请勿进 为注册表的重要键值上把“锁”
光化学反应—串联质谱法鉴定细胞中不饱和卵磷脂双键的位置
什么油适合做炸鸡?
一键直达 Windows 10注册表编辑高招
巧用哈希数值传递文件