Сообщество программистов Autodesk в СНГ

ADN Club => AutoCAD .NET API => Тема начата: Алексей (IdeaSoft) от 17-03-2016, 22:27:16

Название: Проверка типа объекта DBObject
Отправлено: Алексей (IdeaSoft) от 17-03-2016, 22:27:16
Есть объект типа DBObject
взятый, к примеру, из коллекции:

Dim dbObjColl As CAD_DBS.DBObjectCollection = transaction.GetAllObjects

1. Как проверить что он является графическим примитивом
2. Проверить что этот примитив к примеру отрезок (Line)

Для класса Entity проверку сделать не проблема
делаю так:
Код - vb.net [Выбрать]
  1. If typeOf Entity is Autodesk.AutoCAD.DatabaseServices.Line Then
  2. ...
  3. End If
  4.  
Название: Re: Проверка типа объекта DBObject
Отправлено: Вильдар от 17-03-2016, 22:30:50
Например, можно его открыть как Line, и если открытый объект не будет равен null, то это линия.
Причем, это как ни странно, работает быстрее всяких проверок, типа rxclass.
Ну, т.е. что-то типа var line = dbo as Line
Название: Re: Проверка типа объекта DBObject
Отправлено: Алексей (IdeaSoft) от 17-03-2016, 22:33:11
Хорошо, попробую так сделать. Посмотрю что будет.
Название: Re: Проверка типа объекта DBObject
Отправлено: Алексей (IdeaSoft) от 17-03-2016, 22:40:34
открыть как Line
Открыть - это означает вызвать метод transaction.GetObject(...)?
Потому как просто приравнять не получается - выдается исключение.
Название: Re: Проверка типа объекта DBObject
Отправлено: Алексей (IdeaSoft) от 17-03-2016, 22:49:06
var line = dbo as Line
Нет так не получается, т.к. не возможно преобразовать объект BlockTableRecord в Line
Название: Re: Проверка типа объекта DBObject
Отправлено: trir от 17-03-2016, 23:07:59
Код - Visual Basic [Выбрать]
  1.          
  2.             Using tr As Transaction = acDoc.Database.TransactionManager.StartTransaction
  3.                 Try
  4.                     For Each objId As ObjectId In objIdArray
  5.                         dbObj = tr.GetObject(objId, OpenMode.ForRead)
  6.                         'Сортируем полученные объекты
  7.                        Select Case True
  8.                             Case TypeOf dbObj Is Line
  9.                                 wList.Add(dbObj)
  10.                             Case TypeOf dbObj Is Polyline
  11.                                 wList.AddRange(MyTable.PolyToLine(dbObj))
  12.                             Case TypeOf dbObj Is DBText
  13.                                 wTList.Add(dbObj)
  14.                             Case TypeOf dbObj Is MText
  15.                                 wMTList.Add(dbObj)
  16.                         End Select
  17.                     Next
  18.                     tr.Commit()
  19.                 Catch ex As Exception
  20.                     ed.WriteMessage(ex.ToString())
  21.                     tr.Abort()
  22.                 End Try
  23.             End Using
https://habrahabr.ru/post/278765/
Название: Re: Проверка типа объекта DBObject
Отправлено: Алексей (IdeaSoft) от 18-03-2016, 08:35:24
Да именно и через GetObject у меня и получилось.
Только вот в коде выше (в сообщ. от участника форума trir) не понятно возникновение переменной objIdArray?
Не указано из какой именно таблицы базы данных взят набор objIdArray.
Вот мой фрагмент кода
Код - vb.net [Выбрать]
  1. Imports CAD_DBS = Autodesk.AutoCAD.DatabaseServices
  2. ' ...
  3.             Using tr As CAD_DBS.Transaction = db.TransactionManager.StartTransaction
  4.                 Dim bt As CAD_DBS.BlockTable = tr.GetObject(db.BlockTableId, CAD_DBS.OpenMode.ForRead)
  5.                 Dim btr As CAD_DBS.BlockTableRecord = tr.GetObject(bt.Item(CAD_DBS.BlockTableRecord.ModelSpace), CAD_DBS.OpenMode.ForRead)
  6.                 For Each id As CAD_DBS.ObjectId In btr
  7.                     Dim dbObj As CAD_DBS.DBObject = tr.GetObject(id, CAD_DBS.OpenMode.ForRead)
  8.                     If dbObj Is Nothing Then
  9.                         If TypeOf dbObj Is CAD_DBS.Line Then
  10.                             '...
  11.                         End If
  12.                     End If
  13.                 Next
  14.                 tr.Commit()
  15.             End Using
  16.  

И тут у меня другой вопрос. Функция
tr.GetObject(id, CAD_DBS.OpenMode.ForRead)
всегда отработает без сбоев?
Или все же есть вероятность, что в базе
данных может быть ошибка и объект не будет возвращен.
Решил все же вставить проверку на DbObj. 
Название: Re: Проверка типа объекта DBObject
Отправлено: trir от 18-03-2016, 09:12:08
Цитировать
Только вот в коде выше (в сообщ. от участника форума trir) не понятно возникновение переменной objIdArray?
Не указано из какой именно таблицы базы данных взят набор objIdArray.
однако я специально дал ссылку на habr (https://habrahabr.ru/post/278765/), где можно было найти ссылку на github (https://github.com/triroakenshield/tblPrs/blob/master/TableParser/MyTable.vb)
Название: Re: Проверка типа объекта DBObject
Отправлено: Алексей (IdeaSoft) от 18-03-2016, 09:15:30
Теперь понятно откуда берется объект objIdArray
Название: Re: Проверка типа объекта DBObject
Отправлено: trir от 18-03-2016, 09:16:52
174 Public Shared Function CrTbl(acDoc As MyAcAs.Document) As MyTable
Название: Re: Проверка типа объекта DBObject
Отправлено: Привалов Дмитрий от 18-03-2016, 11:52:19
Как проверить что он является графическим примитивом
Код - C# [Выбрать]
  1. DBObject dbObj ....
  2. if (dbObj is Entity)
проверка гарантирует, что объект - графический примитив.

2. Проверить что этот примитив к примеру отрезок (Line)
Проверка конкретных типов примитивов уже не столь однозначна.
Код - C# [Выбрать]
  1. if (dbObj is DBText)
гарантирует, что объект относится к типу DBText, но не гарантирует что сам объект DBText.

Это связанно с иерархией наследования:
специально приведу пример-исключение чтобы было понятно, что исключений мало, но они есть!
AttributeReference : DBText : Entity : DBObject

Для объекта AttributeReference, все проверки вернут true:
Код - C# [Выбрать]
  1. if (dbObj is AttributeReference )
  2. if (dbObj is DBText)
  3. if (dbObj is Entity)
  4. if (dbObj is DBObject)
т.е. выискивая DBText ты зацепишь AttributeReference

В связи с этим для 95% уверенности получаю имя класса примитива и сравниваю.
Код - C# [Выбрать]
  1. Entity entity ....
  2. Type type = entity.GetType();
  3. string typeName = type.Name;
  4. if (typeName == "DBText" )
...такая проверка гарантирует, что тип этого объекта точно не AttributeReference.

..однако не гарантирует, что этот объект относится именно к классу
Autodesk.AutoCAD.DatabaseServices.DBText
а не вашему классу с аналогичным именем MyNewObjects.Entity.DBText

..и если хочешь убедиться на 100%
Код - C# [Выбрать]
  1. Entity entity ....
  2. Type type = entity.GetType();
  3. string fullTypeName = type.ToString();
  4. if (fullTypeName == "Autodesk.AutoCAD.DatabaseServices.DBText" )
...надежнее, но читается не очень

проверки со стрингами(string) не оптимальны по производительности.
В конретном случае если хочешь получить именно DBText, то:
Код - C# [Выбрать]
  1. if ((dbText is DBText) && !(dbText is AttributeReference))
такая проверка должна быть быстрее, но менее универсальна и менее надежна.

Так что выбирай сам, что и когда использовать ;-)
Название: Re: Проверка типа объекта DBObject
Отправлено: Привалов Дмитрий от 18-03-2016, 11:56:59
а как удалить лишнее сообщение?)
Название: Re: Проверка типа объекта DBObject
Отправлено: Дмитрий Загорулькин от 18-03-2016, 16:06:25
Я обычно использую RXClass для проверки типа объекта:
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Runtime;
  5.  
  6. namespace AcadTests
  7. {
  8.     public class DBObjectTest
  9.     {
  10.         [CommandMethod("DBObjectTest")]
  11.         public void Run()
  12.         {
  13.             Document adoc = Application.DocumentManager.MdiActiveDocument;
  14.             Database db = adoc.Database;
  15.             Editor ed = adoc.Editor;
  16.  
  17.             PromptEntityResult entRes = ed.GetEntity("\nВыберите объект: ");
  18.             if (entRes.Status != PromptStatus.OK) return;
  19.  
  20.             bool isText;
  21.  
  22.             using (Transaction tr = db.TransactionManager.StartTransaction())
  23.             {
  24.                 DBObject obj = tr.GetObject(entRes.ObjectId, OpenMode.ForRead);
  25.  
  26.                 RXClass dbTextRx = RXClass.GetClass(typeof(DBText));
  27.  
  28.                 isText = obj.GetRXClass().Equals(dbTextRx);
  29.  
  30.                 tr.Commit();
  31.             }
  32.  
  33.             ed.WriteMessage("\nЭто {0}.", isText ? "текст" : "не текст");
  34.         }
  35.     }
  36. }
Название: Re: Проверка типа объекта DBObject
Отправлено: Привалов Дмитрий от 18-03-2016, 17:03:26
 
Я обычно использую RXClass для проверки типа объекта:
Тоже хороший вариант. И возможно более правильный
Попозже проверю для интереса что быстрее.


...проверил 10 замеров каждого теста. Искал объекты DbText в чертеже 30 мБт
время перебора всех объектов, без проверок типа взял за 100%
100% - перебор всех блоков/объектов в них.
100,24%  - if (!(obj is AttributeReference) && (obj is DBText)) AttrCount++;
108,95% - if (obj.GetType().Name == "DBText") AttrCount++;
125,70% - if (obj.GetRXClass() == dbTextRxClass) AttrCount++;

1й почти не сказывается на производительности, но вернул не правильное значение, 20178 вместо 20064 копаться не буду, что за объект наследованный от DbText в чертеже, для примерочной оценки это незначительно.
2й +9% терпимо и результат поиска совпадает с 3м вариантом
3й самый медленный оказался, +25% скорее всего obj.GetRXClass() работает медленно!

доп. инфа автокад 2008..возможно в новых версиях GetRXClass() быстрее ;-)
Название: Re: Проверка типа объекта DBObject
Отправлено: Владимир Шу от 21-03-2016, 14:48:00
125,70% - if (obj.GetRXClass() == dbTextRxClass) AttrCount++;
Вот не верю я в это, ИМХО Вы что то не так сделали, вся прелесть RXClass в том, что для его получения не нужно получать и открывать объект, достаточно ObjectId, соответственно скорость должна быть намного выше.
Код - C# [Выбрать]
  1.     Db.ObjectId objectId = Db.ObjectId.Null;
  2.     // ...
  3.     Rt.RXClass dimenClass = Rt.RXObject.GetClass(typeof(Db.Dimension));
  4.     if(objectId.ObjectClass.IsDerivedFrom(dimenClass)) {
  5.       // ...
  6.     }
Как видно и транзакция то не нужна...

Вы не могли бы код опубликовать?
Название: Re: Проверка типа объекта DBObject
Отправлено: bargool от 21-03-2016, 15:03:55
Только хотел про ObjectId и RXClass написать, да Boxa.Shu опередил ))
Подтверждаю, пользуюсь именно этим способом, прежде чем открывать объекты - фильтрую по ObjectId и RXClass
Название: Re: Проверка типа объекта DBObject
Отправлено: Привалов Дмитрий от 21-03-2016, 15:15:47
Как видно и транзакция то не нужна...
Для 2008 это увы не так. метода ObjectClass у ObjectId нет. Соответственно приходится получить объект, а затем вызывать GetRXClass() объекта.
А я вынужден писать универсальный код. А так вы правы, будет быстрее, за счет уменьшения количества медленных методов tr.GetObject(id...)

Я использую свои доп классы для подключения к БД и управления транзакцией, но надеюсь смысл понятен из кода

Код - C# [Выбрать]
  1. using (DbConnection dwg = new DbConnection(DbFlag.Current))
  2. {
  3.         List<BlockTableRecord> blocks = dwg.GetBlocks(OpenMode.ForRead, false);
  4.  
  5.         RXClass dbText = RXClass.GetClass(typeof(DBText));
  6.         int AttrCount = 0;
  7.         Stopwatch timer1 = new Stopwatch();
  8.         timer1.Start();
  9.                        
  10.         foreach (BlockTableRecord block in blocks)
  11.         {
  12.                 foreach (ObjectId id in block)
  13.                 {
  14.                         DBObject obj = dwg.GetObject(id, OpenMode.ForWrite, false, true);
  15.                                                
  16.                         if (obj.GetRXClass() == dbText) AttrCount++;
  17.                 }
  18.         }
  19.         timer1.Stop();
  20.         long seconds = timer1.ElapsedMilliseconds;
  21.         Logger.WriteMessage("Считано атрибутов-{0}/время-{1}", AttrCount, seconds);
  22. }
Название: Re: Проверка типа объекта DBObject
Отправлено: Владимир Шу от 21-03-2016, 15:39:24
Для 2008 это увы не так. метода ObjectClass у ObjectId нет. Соответственно приходится получить объект, а затем вызывать GetRXClass() объекта.
Посмотрел, нда... objectId.ObjectClass только в 2009 появился... тогда нет смысл получать тип через этот механизм... если только Александр Ривилис не поможет получить этот метод через PInvoke ...
Название: Re: Проверка типа объекта DBObject
Отправлено: Дмитрий Загорулькин от 21-03-2016, 16:05:54
В связи с этим для 95% уверенности получаю имя класса примитива и сравниваю.
..и если хочешь убедиться на 100%
Код - C# [Выбрать]
  1. Entity entity ....
  2. Type type = entity.GetType();
  3. string fullTypeName = type.ToString();
  4. if (fullTypeName == "Autodesk.AutoCAD.DatabaseServices.DBText" )
...надежнее, но читается не очень
Для 2008 это увы не так. метода ObjectClass у ObjectId нет.
Ну тогда уж, наверное, лучше так:
Код - C# [Выбрать]
  1. using AcDb = Autodesk.AutoCAD.DatabaseServices;
  2. ...
  3. Type dbTxtType = typeof(AcDb.DBText);
  4. if (entity.GetType().Equals(dbTxtType))
  5. ...
  6.  
Надежно и "читабельно".
Название: Re: Проверка типа объекта DBObject
Отправлено: Привалов Дмитрий от 21-03-2016, 16:13:14
Ну тогда уж, наверное, лучше так:
Ага до Equals типов я не додумался. А ответ был так близко....более корректный вариант, чем сравнение через "is"
Название: Re: Проверка типа объекта DBObject
Отправлено: Дмитрий Загорулькин от 21-03-2016, 16:15:19
Интересно только, как он по скорости будет. Быстрее, чем сравнение с именем типа или нет?
Название: Re: Проверка типа объекта DBObject
Отправлено: Привалов Дмитрий от 21-03-2016, 16:24:26
Интересно только, как он по скорости будет. Быстрее, чем сравнение с именем типа или нет?
Под автокадом проверил он самый быстрый и корректный в моем случае(без objectId.ObjectClass разумеется)
Он +- равен Is по скорости. Разница слишком мала, чтоб в автокаде ее выловить.
Если уж хочется проверить более точно, то в чистом C# без автокада нужно мерять.
Название: Re: Проверка типа объекта DBObject
Отправлено: Привалов Дмитрий от 21-03-2016, 16:35:45
Быстрее, чем сравнение с именем типа или нет?
Протестил, не быстрее.
1877 - is
12812 - GetType().Equals()

Но в реальном приложении эту разницу не выловить, поэтому не стоит париться.
Код - C# [Выбрать]
  1. Stopwatch timer1 = new Stopwatch();
  2. timer1.Start();
  3.  
  4. int k = 10;
  5. for (int i = 0; i < 1000000000; i++)
  6. {
  7.         if (k is int)
  8.         {
  9.         }
  10. }
  11. timer1.Stop();
  12.  
  13. long seconds = timer1.ElapsedMilliseconds;
  14. Console.WriteLine("{0} - is int", seconds);
  15.  
  16. Stopwatch timer2 = new Stopwatch();
  17. timer2.Start();
  18.  
  19.  
  20. Type intt = typeof(int);
  21. for (int i = 0; i < 1000000000; i++)
  22. {
  23.         if (k.GetType().Equals(intt))
  24.         {
  25.         }
  26. }
  27. timer2.Stop();
  28.  
  29. long seconds2 = timer2.ElapsedMilliseconds;
  30. Console.WriteLine("{0} - GetType().Equals()", seconds2);
  31.  
  32. Console.ReadKey();
Название: Re: Проверка типа объекта DBObject
Отправлено: Александр Ривилис от 21-03-2016, 17:26:22
Посмотрел, нда... objectId.ObjectClass только в 2009 появился... тогда нет смысл получать тип через этот механизм... если только Александр Ривилис не поможет получить этот метод через PInvoke ...
В ObjectARX этот метод появился синхронно с AutoCAD .NET API в 2009-ой версии.