Как создать объект AutoCAD с помощью лямбда-выражения и вернуть его - PullRequest
0 голосов
/ 12 декабря 2018

Я новичок в программировании на C # (и программировании в целом), но я начинаю разбираться с разработкой AutoCAD с использованием AutoDesk .NET API для проектов на работе.

Есть определенные повторяющиеся задачив AutoCAD dev, для которого я создавал вспомогательные методы, чтобы упростить мой код.Чтобы создать объект (линии, полилинии, аннотации и т. Д.) В AutoCAD через .API, программист должен написать довольно запутанный оператор, который обращается к среде AutoCAD, получает текущий чертеж, получает базу данныхтекущий файл чертежа, запускает транзакцию с базой данных //do work, затем добавляет созданные объекты в базу данных перед окончательным подтверждением и закрытием транзакции.

Поэтому я написал следующий код, чтобы упростить эту задачу:

public static void CreateObjectActionWithinTransaction(Action<Transaction, Database, BlockTable, BlockTableRecord> action)
{
    var document = Application.DocumentManager.MdiActiveDocument;
    var database = document.Database;
    using (var transaction = document.TransactionManager.StartTransaction())
    {
        BlockTable blocktable = transaction.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;
        BlockTableRecord blockTableRecord = transaction.GetObject(blocktable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
        action(transaction, database, blocktable, blockTableRecord);

        transaction.Commit();
    }
}

Затем мое лямбда-выражение, которое создает универсальный MText и устанавливает для него некоторые параметры:

public static void createMtext(Point3d location, AttachmentPoint attachmentpoint, string contents, double height, short color, bool usebackgroundmask, bool usebackgroundcolor, double backgroundscale)
{
    CreateObjectActionWithinTransaction((transaction, database, blocktable, blocktablerecord) =>
    {
        MText mt = new MText();
        mt.SetDatabaseDefaults();
        mt.Location = location;
        mt.Attachment = attachmentpoint;
        mt.Contents = contents;
        mt.Height = height;
        mt.Color = Color.FromColorIndex(ColorMethod.ByAci, color);
        mt.BackgroundFill = usebackgroundmask;
        mt.UseBackgroundColor = usebackgroundcolor;
        mt.BackgroundScaleFactor = backgroundscale;
        blocktablerecord.AppendEntity(mt);
        transaction.AddNewlyCreatedDBObject(mt, true);
    });
}

И, наконец, когда я на самом деле создаю MText где-то,Я могу создать его в одну строку и передать значения для всех параметров без необходимости выписывать огромный код транзакции для него:

Helpers.createMtext(insertpoint, AttachmentPoint.MiddleLeft, "hello world", .08, colors.AutoCAD_Red, true, true, 1.2);

Так что это здорово, и это работает, когда я хочу создатьMText сам и положи куда-нибудь.Однако в некоторых других ситуациях вместо создания MText и размещения его на чертеже я хочу создать MText, используя ту же базовую предпосылку, что и выше, но вернуть ее в качестве значения, которое будет использоваться где-то еще..

В AutoCAD есть объекты аннотаций, называемые Multileaders, которые, по сути, просто MText, как и выше, но прикреплены к некоторым линиям и стрелке, указывающей на что-либо на чертеже.В API необходимо определить MText и присоединить его к объекту Multileader.Однако мой приведенный выше код не может быть использован, потому что он ничего не возвращает.

Так что мой вопрос сводится к тому, как я могу создать метод, подобный выше, для создания объекта, но вместо того, чтобы просто создать этот объектВозвращает ли он этот объект для использования другим фрагментом кода?

Также есть ли хорошие ресурсы для начинающих по лямбда-выражениям?Книги, сайты, YouTube?

Ответы [ 3 ]

0 голосов
/ 13 декабря 2018

Для детали AutoCAD:

Как сказал Мииир в комментарии, не возвращайте объект, а скорее ObjectId.Экземпляр объекта принадлежит транзакции, поэтому если вы откроете объект с помощью какой-либо транзакции, зафиксируете транзакцию и попробуете использовать этот объект в другой транзакции, AutoCAD в основном просто рухнет.

Работа с AutoCAD API всегда следует этому базовому шаблону:

  1. Начать транзакцию
  2. Создать новый объект или использовать транзакцию для получения существующего объекта.Это достигается либо с помощью ObjectID, либо с помощью циклического перебора таблиц и поиска любых интересующих вас атрибутов (например, BlockTable, BlockTableRecord, LayerTable и т. Д.)
  3. объект.
  4. Подтверждение или отмена транзакции.

Если вы попытаетесь обойти шаги 1 и 2, это не сработает.Итак, верните ObjectID, а затем используйте id для получения объекта в другой транзакции.

Что касается части C #:

Если вы хотите вернуть значение с помощью делегата,Action<T> не твой друг.Action не возвращает значение, оно только «действует», то есть имя.Если вы хотите использовать делегата для возврата значения, у вас есть 2 варианта:

  1. Определить пользовательский тип делегата.

  2. Использовать универсальный делегатпоставляется .NET Framework Func<T1,T2,T3,T4,TResult>.

Какой из них вы должны использовать?В вашем случае я бы, вероятно, выбрал вариант 1 по той простой причине, что ваш код будет намного чище и проще в обслуживании.Я буду использовать это в этом примере.Использование Func будет работать точно так же, за исключением того, что сигнатуры вашей функции будут выглядеть немного уродливо.

Пользовательский делегат:

//somewhere in your code inside a namespace (rather than a class)
public delegate ObjectId MyCreateDelegate(Transaction transaction, Database db,
         BlockTable blockTable, BlockTableRecord blockTableRecord);

Тогда ваш общий метод

public static ObjectId CreateObjectActionWithinTransaction(MyCreateDelegate createDel)
{
    ObjectId ret;
    var document = Application.DocumentManager.MdiActiveDocument;
    var database = document.Database;
    using (var transaction = document.TransactionManager.StartTransaction())
    {
        BlockTable blocktable = transaction.GetObject(database.BlockTableId, OpenMode.ForRead) as BlockTable;
        BlockTableRecord blockTableRecord = transaction.GetObject(blocktable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
        //here createMtext will get called in this case, and return ObjectID
        ret = createDel(transaction, database, blocktable, blockTableRecord);
        transaction.Commit();
    }
    return ret;
}

и конкретный метод с лямбдой:

public ObjectId createMtext(Point3d location, AttachmentPoint attachmentpoint, string contents, double height, short color, bool usebackgroundmask, bool usebackgroundcolor, double backgroundscale)
{
    //here you can return the result the general function
    return CreateObjectActionWithinTransaction((transaction, database, blocktable, blocktablerecord) =>
    {
        MText mt = new MText();
        mt.SetDatabaseDefaults();
        mt.Location = location;
        mt.Attachment = attachmentpoint;
        mt.Contents = contents;
        mt.Height = height;
        mt.Color = Color.FromColorIndex(ColorMethod.ByAci, color);
        mt.BackgroundFill = usebackgroundmask;
        mt.UseBackgroundColor = usebackgroundcolor;
        mt.BackgroundScaleFactor = backgroundscale;
        blocktablerecord.AppendEntity(mt);
        transaction.AddNewlyCreatedDBObject(mt, true);
        //make sure to get ObjectId only AFTER adding to db.
        return mt.ObjectId;
    });
}

И, наконец, используйте его следующим образом

ObjectId mtId = Helpers.createMtext(insertpoint, AttachmentPoint.MiddleLeft, "hello world", .08, colors.AutoCAD_Red, true, true, 1.2);
//now use another transaction to open the object and do stuff to it.

Учебные ресурсы:

И, наконец, чтобы понять лямбдавыражений, вы должны начать с понимания делегатов, если вы этого еще не сделали.Все лямбды - это синтаксический сахар для создания экземпляра объекта делегата, который указывает либо на метод, либо на анонимный метод, как вы сделали в своем примере. Этот урок выглядит довольно хорошо.И помните, такие делегаты, как Action, Func и Predicate, или ничем не отличаются.Поэтому, независимо от того, определяете ли вы свой собственный делегат или используете готовое решение, лямбда-выражения не имеют значения.

Для обзора лямбды обратитесь к этому руководству .

Не ограничивайте себя двумя источниками, которые я предоставил.Просто Google это, и лучшие 10 хитов будут довольно хорошей информацией.Вы также можете проверить Pluralsight .Я много учусь там.

0 голосов
/ 13 декабря 2018

Вместо использования делегатов, я бы предпочел использовать методы расширения, вызываемые из транзакции в вызывающем методе.

static class ExtensionMethods
{
    public static BlockTableRecord GetModelSpace(this Database db, OpenMode mode = OpenMode.ForRead)
    {
        var tr = db.TransactionManager.TopTransaction;
        if (tr == null)
            throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);
        return (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), mode);
    }

    public static void Add(this BlockTableRecord btr, Entity entity)
    {
        var tr = btr.Database.TransactionManager.TopTransaction;
        if (tr == null)
            throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.NoActiveTransactions);
        btr.AppendEntity(entity);
        tr.AddNewlyCreatedDBObject(entity, true);
    }

    public static MText AddMtext(this BlockTableRecord btr, 
        Point3d location, 
        AttachmentPoint attachmentpoint, 
        string contents, 
        double height, 
        short color = 256, 
        bool usebackgroundmask = false, 
        bool usebackgroundcolor = false, 
        double backgroundscale = 1.5)
    {
        MText mt = new MText();
        mt.SetDatabaseDefaults();
        mt.Location = location;
        mt.Attachment = attachmentpoint;
        mt.Contents = contents;
        mt.Height = height;
        mt.ColorIndex = color;
        mt.BackgroundFill = usebackgroundmask;
        mt.UseBackgroundColor = usebackgroundcolor;
        mt.BackgroundScaleFactor = backgroundscale;
        btr.Add(mt);
        return mt;
    }
}

Используя пример:

        public static void Test()
    {
        var doc = AcAp.DocumentManager.MdiActiveDocument;
        var db = doc.Database;
        using (var tr = db.TransactionManager.StartTransaction())
        {
            var ms = db.GetModelSpace(OpenMode.ForWrite);
            var mt = ms.AddMtext(Point3d.Origin, AttachmentPoint.TopLeft, "foobar", 2.5);
            // do what you want with 'mt'
            tr.Commit();
        }
    }
0 голосов
/ 12 декабря 2018

Я не знаком с AutoCad API, но похоже, что "action.Commit () "- это строка, которая фактически выполняет действие по размещению MText в вашей модели.

, если это так;я бы сделал что-то вроде следующего:

public MText CreateMTextObject({parameters})
{
//code
  Return textObject
}

public PlaceTextObject({parameters})
{
  CreateTextObject(parameters).Commit()
}

Таким образом, вы можете оставить текстовый объект для дальнейших манипуляций, оставив при этом возможность применить его за один раз.Он также будет иметь только один кодовый блок для создания объекта, что гарантирует отсутствие различий в реализации между двумя методами

...