ashSet的基本概念
- HashSet 是C#中用于存储唯一元素的泛型集合类,它基于哈希表实现,具有快速查找、添加和删除元素的特性。
- 核心特点:
- 不允许存储重复元素
- 元素无序排列
- 查找、添加、删除操作的平均时间复杂度为O(1)
- 实现了 IEnumerable<T> 和 ICollection 接口
常用属性和方法
- 属性:
- Count :获取HashSet中包含的元素数量
- 主要方法:
- Add(T item) :向HashSet中添加元素,若元素已存在则添加失败
- Contains(T item) :判断HashSet是否包含指定元素
- Remove(T item) :从HashSet中移除指定元素
- UnionWith(IEnumerable<T> other) :将两个集合的并集存储到当前集合
- IntersectWith(IEnumerable<T> other) :将两个集合的交集存储到当前集合
- SymmetricExceptWith(IEnumerable<T> other) :将两个集合的对称差集存储到当前集合
- IsSubsetOf(IEnumerable<T> other) :判断当前集合是否为另一个集合的子集
在CAD二次开发中的应用场景
在AutoCAD二次开发中,HashSet常用于:
- 管理图形对象集合(避免重复添加相同对象)
- 快速查找和筛选特定类型的图元
- 处理图形对象的集合运算(如求交、求并)
- 记录已处理的对象,避免重复操作
具体应用实例与代码
下面是一个在AutoCAD二次开发中使用HashSet的完整示例,演示了如何使用HashSet管理图形对象、进行对象筛选和集合运算:
using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.EditorInput;
[assembly: CommandClass(typeof(HashSetInCAD.Commands))]
namespace HashSetInCAD
{
public class Commands
{
// 示例命令:使用HashSet管理和操作CAD图形对象
[CommandMethod("UseHashSetInCAD")]
public void UseHashSetInCAD()
{
// 获取当前文档和数据库
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
try
{
// 在事务中处理数据库操作
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 获取模型空间
BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
BlockTableRecord modelSpace = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
// 1. 创建HashSet存储圆对象
HashSet<ObjectId> circleIds = new HashSet<ObjectId>();
// 2. 向模型空间添加多个圆,并将其ID存入HashSet
for (int i = 0; i < 5; i++)
{
// 创建圆
Circle circle = new Circle();
circle.Center = new Point3d(i * 2, 0, 0);
circle.Radius = 1.0;
// 添加到模型空间
modelSpace.AppendEntity(circle);
tr.AddNewlyCreatedDBObject(circle, true);
// 将圆的ID添加到HashSet
circleIds.Add(circle.ObjectId);
ed.WriteMessage($"\n已添加圆,ID: {circle.ObjectId}");
}
// 3. 尝试添加重复的圆(验证HashSet去重特性)
Circle duplicateCircle = new Circle();
duplicateCircle.Center = new Point3d(2, 0, 0); // 与i=1时的圆位置相同
duplicateCircle.Radius = 1.0;
modelSpace.AppendEntity(duplicateCircle);
tr.AddNewlyCreatedDBObject(duplicateCircle, true);
// 尝试添加到HashSet(由于已有相同位置的圆,添加会失败)
bool added = circleIds.Add(duplicateCircle.ObjectId);
ed.WriteMessage($"\n添加重复圆结果: {(added ? "成功" : "失败,已存在重复元素")}");
// 4. 使用HashSet进行对象筛选 - 查找半径大于1.5的圆(假设存在)
// 这里先创建一个半径大于1.5的圆用于演示
Circle largeCircle = new Circle();
largeCircle.Center = new Point3d(10, 0, 0);
largeCircle.Radius = 2.0;
modelSpace.AppendEntity(largeCircle);
tr.AddNewlyCreatedDBObject(largeCircle, true);
// 创建另一个HashSet存储大半径圆
HashSet<ObjectId> largeCircles = new HashSet<ObjectId>();
// 遍历模型空间所有实体,筛选半径大于1.5的圆
foreach (ObjectId objId in modelSpace)
{
Entity entity = (Entity)tr.GetObject(objId, OpenMode.ForRead);
if (entity is Circle circleObj && circleObj.Radius > 1.5)
{
largeCircles.Add(objId);
}
}
ed.WriteMessage($"\n找到{largeCircles.Count}个半径大于1.5的圆");
// 5. 集合运算 - 求两个集合的交集
HashSet<ObjectId> intersection = new HashSet<ObjectId>(circleIds);
intersection.IntersectWith(largeCircles);
ed.WriteMessage($"\n两个集合的交集有{intersection.Count}个元素");
// 6. 使用HashSet优化对象遍历 - 快速判断对象是否已处理
HashSet<ObjectId> processedIds = new HashSet<ObjectId>();
// 模拟处理对象的过程
foreach (ObjectId objId in modelSpace)
{
if (processedIds.Contains(objId))
continue; // 已处理过的对象跳过
processedIds.Add(objId);
// 这里可以添加对象处理逻辑
ed.WriteMessage($"\n处理对象: {objId}, 类型: {tr.GetObject(objId, OpenMode.ForRead).GetType().Name}");
}
tr.Commit();
}
}
catch (System.Exception ex)
{
ed.WriteMessage($"\n错误: {ex.Message}");
}
}
// 另一个示例:使用HashSet管理图层
[CommandMethod("ManageLayersWithHashSet")]
public void ManageLayersWithHashSet()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
try
{
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 获取图层表
LayerTable lt = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForWrite);
// 创建HashSet存储图层名
HashSet<string> layerNames = new HashSet<string>
{
"Layer1",
"Layer2",
"Layer3"
};
// 创建图层(如果不存在)
foreach (string layerName in layerNames)
{
if (!lt.Has(layerName))
{
LayerTableRecord ltr = new LayerTableRecord();
ltr.Name = layerName;
lt.Add(ltr);
tr.AddNewlyCreatedDBObject(ltr, true);
ed.WriteMessage($"\n创建图层: {layerName}");
}
}
// 检查是否存在未在HashSet中的图层
HashSet<string> existingLayers = new HashSet<string>();
foreach (DBDictionaryEntry entry in lt)
{
existingLayers.Add(entry.Key);
}
// 找出不在目标集合中的图层
HashSet<string> extraLayers = new HashSet<string>(existingLayers);
extraLayers.ExceptWith(layerNames);
if (extraLayers.Count > 0)
{
ed.WriteMessage($"\n发现{extraLayers.Count}个多余图层:");
foreach (string layer in extraLayers)
{
ed.WriteMessage($"\n- {layer}");
// 这里可以添加删除图层的逻辑
}
}
tr.Commit();
}
}
catch (System.Exception ex)
{
ed.WriteMessage($"\n错误: {ex.Message}");
}
}
}
}
代码解析与关键应用点
1. 对象唯一性管理:
- 通过 HashSet<ObjectId> 存储图形对象ID,自动避免重复添加相同对象
- 示例中尝试添加重复圆时,HashSet的 Add 方法返回false,体现了去重特性
2. 高效对象筛选:
- 遍历模型空间时,利用HashSet的快速查找特性( Contains 方法)判断对象是否已处理
- 通过条件筛选(如半径大于1.5的圆),将符合条件的对象存入新的HashSet
3. 集合运算应用:
- 使用 IntersectWith 方法计算两个对象集合的交集
- 还可扩展使用并集( UnionWith )、差集( ExceptWith )等运算
4. 图层管理场景:
- 第二个示例展示了如何使用HashSet管理图层,确保文档中只存在指定图层
- 通过集合运算找出多余图层,为批量处理图层提供了高效方案
在CAD开发中使用HashSet的优势
1. 性能优势:
- 相比List等集合,HashSet的查找、添加、删除操作效率更高,尤其适合处理大量图形对象
2. 逻辑简化:
- 自动去重特性简化了对象管理逻辑,避免了手动判断重复的繁琐代码
3. 集合运算便捷:
- 内置的集合运算方法(交、并、差)使复杂的对象筛选和管理变得简单
4. 内存优化:
- 相比存储完整对象,HashSet存储对象ID更节省内存,适合处理大型CAD文档
使用上述代码时,需确保已正确引用AutoCAD相关程序集,并将代码编译为DLL后加载到AutoCAD中使用。根据实际项目需求,还可以进一步扩展HashSet的应用场景,如在块参照管理、图形比较等功能中发挥作用。
收集唯一图层名
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using System.Collections.Generic;
public class LayerCollector
{
public static void CollectUniqueLayers()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 创建HashSet存储唯一图层名
HashSet<string> uniqueLayers = new HashSet<string>();
// 获取模型空间块表记录
BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord modelSpace = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord;
// 遍历所有实体
foreach (ObjectId entityId in modelSpace)
{
Entity entity = tr.GetObject(entityId, OpenMode.ForRead) as Entity;
if (entity != null)
{
// 自动过滤重复图层名
uniqueLayers.Add(entity.Layer);
}
}
// 输出结果
ed.WriteMessage("\n唯一图层数量: {0}", uniqueLayers.Count);
foreach (string layer in uniqueLayers)
{
ed.WriteMessage("\n图层: {0}", layer);
}
tr.Commit();
}
}
}
检测重复块引用
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using System.Collections.Generic;
public class BlockDuplicateChecker
{
public static void CheckDuplicateBlockReferences()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
// 存储块引用位置和块名
HashSet<string> blockSignatures = new HashSet<string>();
BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
BlockTableRecord modelSpace = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord;
foreach (ObjectId id in modelSpace)
{
if (id.ObjectClass == General.Class(typeof(BlockReference)))
{
BlockReference br = tr.GetObject(id, OpenMode.ForRead) as BlockReference;
string signature = $"{br.Name}_{br.Position.ToString()}"; // 唯一签名
// 检测重复
if (!blockSignatures.Add(signature))
{
// 发现重复块引用
br.Highlight(); // 高亮显示重复项
}
}
}
tr.Commit();
}
}
}
快速过滤选择集
using Autodesk.AutoCAD.EditorInput;
using System.Collections.Generic;
public class FastSelectionFilter
{
public static void SelectUniqueEntities()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
// 用户选择对象
PromptSelectionResult selRes = ed.GetSelection();
if (selRes.Status != PromptStatus.OK) return;
// 使用HashSet去重
HashSet<SelectedObject> uniqueSelection = new HashSet<SelectedObject>();
foreach (SelectedObject selObj in selRes.Value)
{
// 自定义去重逻辑:按句柄去重
if (selObj != null)
{
string handle = selObj.ObjectId.Handle.ToString();
if (!uniqueSelection.Contains(selObj))
{
uniqueSelection.Add(selObj);
}
}
}
// 创建新选择集
ObjectId[] ids = new ObjectId[uniqueSelection.Count];
int i = 0;
foreach (SelectedObject selObj in uniqueSelection)
{
ids[i++] = selObj.ObjectId;
}
// 应用过滤后的选择集
ed.SetImpliedSelection(ids);
}
}
# **五、与传统集合对比**
| 操作 | HashSet | List | Dictionary |
|---------------|--------------|-------------|------------------|
| 添加元素 | O(1) | O(1) | O(1) |
| 查找元素 | O(1) | O(n) | O(1) |
| 删除元素 | O(1) | O(n) | O(1) |
| 内存占用 | 中等 | 低 | 高 |
| 有序性 | 否 | 是 | 否 |
---
通过合理应用`HashSet`,可以在CAD二次开发中显著提升处理大量实体数据的效率,特别是在需要频繁进行存在性检查或去重操作的场景中表现优异。建议结合具体业务需求选择合适的数据结构。