起因:
在学习在CAD中附加自定义的数据的时候,发现的一个知识点。在组码表中,330,340,350,360四个键分别对应四种关系。
如下图:
在DataCell里也有类似的操作:
总结起来,就是在CAD中,我们可以人为地给两个DBObject建立某种关系,即:软指针,硬指针,软所有者,硬所有者。
后来,在CAD官方的帮助页面找了一下,找到了官方的解释。
官方解释翻译过来是:
将对象写入DXF或DWG文件时,该对象拥有的所有对象也会被写出。深度克隆操作还递归地复制克隆对象所拥有的每个对象。(参见深度克隆。)硬所有权关系保护所拥有的对象免受清除。 所有者可以是其对象的硬所有者或软所有者。
以下是硬所有权的三个例子:
数据库对象是其扩展字典的硬所有者。
块表是模型空间和图纸空间块表记录(但不是其他块表记录)的硬所有者。
扩展字典是其元素的硬所有者。
软所有权ID(类型为AcDbSoftOwnershipId)不会保护所拥有的对象免受清除。以下是软所有权的示例:
在大多数情况下,符号表是其元素的软所有者(例外情况包括块*MODEL_SPACE、*PAPER_SPACE、*PAPER_SPACE0和层0;对于这些元素,符号表维护一个硬引用)。
词典是其条目的软所有者(但您可以将词典标记为其条目的硬所有者)。
您的自定义类还可能包含对数据库中其他对象的硬指针或软指针引用。指针是单向链接(也就是说,引用的对象中没有指示指针来源的信息)。一个对象可以指向或被任意数量的其他对象指向。
硬指针引用保护对象免受清除。例如,实体包含对层的硬指针引用。因此,您无法清除由一个或多个实体指向的图层。当从现有数据库中写出新数据库时(例如,在WBLOCK操作中),所有硬指针都会复制到新数据库中。
硬指针引用的其他示例
引线图元包含指向标注样式的硬指针引用。
文本实体包含对文本样式的硬指针引用。
标注图元包含指向标注样式的硬指针引用。
mline实体具有指向mline样式的硬指针引用。
软指针只是指向对象的指针。它不会保护引用的对象免受清除。
软指针引用示例
Xdata引用是软指针。
持久性反应堆是软指针。
如果使用软指针引用对象,则应在打开对象之前检查该对象是否仍然存在。
个人理解:
实际上,上面这段官方解释是分类在ObjectArx-Custom Objects里的,意味着这个知识点主要是在自定义实体中会使用到。而.NET语言不具备自定义实体开发能力,因此使用.NET开发的插件中的运行情况和上述描述不是完全对得上的。
官方资料显示,一个DBObject只能有一个Owner,除了直属于Database的九大符号表和有名词典外,所有DBObject都应该有个Owner,Owner是这个对象能存在于数据库中的必要条件;因此,要把一个DBObject加入到Database中,一定是指定了一个Owner的。通常情况下,一个DBObject对象的Owner是它的容器对象。比如,模型空间内的一个Entity,它的Owner是模型空间BlockTableRecord;一个LayerTableRecord对象,它的Owner是LayerTable对象;一个附加在Entity上的DBDictionary对象(Entity.ExtensionDictionary),Entity是DBDictionary的Owner,DBDictionary使用SetAt()方法加入的对象,其Owner就是这个DBDictionary。删除一个对象的Owner,这个对象被一并删除,比如:删除一个块表记录,那么该记录下的所有Entity都会被删除;删除一个DBObject,存在它的ExtensionDictionary的所有信息也会被一并删除。
Own关系是一个双向关系,给一个对象指定了所有者的时候,这个所有者的列表中也应加入这个被所有对象;删除所有者对象会同时删除被所有对象正是通过读取被所有对象列表的方式实现的;删除被所有对象的时候,需要检查它和所有者的关系是硬所有还是软所有,如果是硬所有,则不能删除它。
在CAD二次开发中,给一个DBObject指定一个Owner的情况应该只会在自定义实体中会用到才对,令人困惑的是这些功能被公开给了.NET语言,实际试用了一下,好像也没报错。
相比Own关系,Pointer关系就简单灵活多了,就是指定了一个引用,通过这个引用,我们可以迅速地找到另一个DBObject。
Pointer关系是一个单向关系,引用对象将这个指针存在自己的列表里,被引用的对象不能反向找到引用它的对象。
硬指针和软指针的区别在于,如果一个对象被一个或多个硬指针指向,那么它将被保护,不被清除;软指针不保护它指向的对象,因此软指针指向的对象在打开之前应该验证它是否仍然存在。
值得注意的是,在CAD.NET中,“硬指针保护它指向的对象不被清除”这件事和事实有点出入。
一个Entity对一个图层的引用是一个硬指针,存在某个Entity引用某个图层时,这个图层是无法被Purge命令或者删除命令删除的;我用一个DataTable对一个图层进行硬指针引用得到了同样的结果。但如果我将一个硬指针指向一个Line,这个Line可以被正常地删除,如果把这个Line长度改为0,也可以被Purge命令清除。因此,我判断,CAD内部只针对特定的DBObject对象进行了硬指针引用检查,实际应用中应注意这一点。
在二次开发的程序中,我们可以把两个DBObject的关系存起来,这个关系可以随CAD环境保存到DWG文件里;下次打开时,可以读取这些关系从而迅速建立自己的数据模型,当然,最好先检查一下这个对象是否存在。
测试代码:
1 public static void WriteHardOwnerDataTableDemo() 2 { 3 var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.CurrentDocument; 4 if (doc == null) return; 5 var db = doc.Database; 6 var ed = doc.Editor; 7 var pResult = ed.GetEntity("选择一个Entity"+"\n"); 8 if (pResult.Status!= Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { ed.WriteMessage("选择Entity失败,退出"+"\n"); return; } 9 ObjectId ownerId = ObjectId.Null; 10 switch (1) 11 { 12 case 1: 13 string layerName = "HardOwnerTestLayer"; 14 using (var trans = db.TransactionManager.StartTransaction()) 15 { 16 var layerTable = trans.GetObject(db.LayerTableId, OpenMode.ForWrite) as LayerTable; 17 if (!layerTable.Has(layerName)) 18 { 19 var layer = new LayerTableRecord(); 20 layer.Name = layerName; 21 layerTable.Add(layer); 22 trans.AddNewlyCreatedDBObject(layer, true); 23 } 24 ownerId = layerTable[layerName]; 25 trans.Commit(); 26 } 27 break; 28 case 2: 29 var pResult2 = ed.GetEntity("选择一个要参照的Entity"+"\n"); 30 if (pResult2.Status!= Autodesk.AutoCAD.EditorInput.PromptStatus.OK) { ed.WriteMessage("选择Entity失败,退出"+"\n"); return; } 31 ownerId = pResult2.ObjectId; 32 break; 33 case 3: 34 35 break; 36 default: 37 38 } 39 40 41 var dataTable = CreatNewDataTable2(ownerId); 42 using (var trans = db.TransactionManager.StartTransaction()) 43 { 44 var ent = trans.GetObject(pResult.ObjectId, Autodesk.AutoCAD.DatabaseServices.OpenMode.ForWrite) as Entity; 45 { 46 //如果ent.ExtensionDictionary没有创建,则创建 47 if (ent.ExtensionDictionary == ObjectId.Null) ent.CreateExtensionDictionary(); 48 var dicID = ent.ExtensionDictionary; 49 var dic = trans.GetObject(dicID, OpenMode.ForWrite) as DBDictionary; 50 dic.SetAt("DataTableDemo2", dataTable); 51 trans.AddNewlyCreatedDBObject(dataTable, true); 52 ed.WriteMessage("写入DataTable2成功"+"\n"); 53 } 54 trans.Commit(); 55 } 56 } 57 private static DataTable CreatNewDataTable2(ObjectId HardShipId) 58 { 59 var dataTable = new DataTable(); 60 dataTable.AppendColumn(CellType.CharPtr, "Name"); 61 dataTable.AppendColumn(CellType.HardPtrId, "OwnerID"); 62 //dataTable.AppendColumn(CellType.HardOwnerId, "OwnerID"); 63 var row = new DataCellCollection(); 64 var cell1 = new DataCell(); 65 cell1.SetString("NameABC"); 66 var cell2 = new DataCell(); 67 cell2.SetHardPointerId(HardShipId); 68 //cell2.SetHardOwnershipId(HardShipId); 69 row.Add(cell1); 70 row.Add(cell2); 71 //AppendRow()方法的第二个参数是是否对加入的这一行进行检查 72 //具体指数量检查和类型检查 73 dataTable.AppendRow(row, true); 74 return dataTable; 75 76 }