乐筑天下

搜索
欢迎各位开发者和用户入驻本平台 尊重版权,从我做起,拒绝盗版,拒绝倒卖 签到、发布资源、邀请好友注册,可以获得银币 请注意保管好自己的密码,避免账户资金被盗
查看: 109|回复: 7

Dynamic .NET

[复制链接]

72

主题

2726

帖子

9

银币

社区元老

Rank: 75Rank: 75Rank: 75

铜币
3014
发表于 2012-7-21 14:54:00 | 显示全部楼层 |阅读模式
Dynamic .NET in  2013
Another really interesting, developer-oriented feature in AutoCAD 2013 is something we’ve been calling “Dynamic .NET”. I don’t know whether that’s official branding, or not – I suspect not – but it’s the moniker we’ve been using to describe this capability internally and to ADN members.
The capability is based on an addition to .NET in version 4.0: to complement (or perhaps just as part of) the integration of the Dynamic Language Runtime into the core .NET Framework, various interfaces – including IDynamicMetaObjectProvider – were provided to developers to let their objects participate in “dynamic” operations.
Providers of APIs therefore now have the option to make certain classes dynamic – for instance, having them implement the IDynamicMetaObjectProvider interface – which means they can effectively be scripted, without binding properties and methods at compile-time. This implements a concept known as duck typing, and is at the core of many scripting language implementations. You may also hear it referred to as late binding: take a look at this excellent post by Eric Lippert if you’d like to get a better understanding of what that term means.
So what have we actually done with AutoCAD 2013? We have made Autodesk.AutoCAD.DatabaseServices.ObjectId support dynamic operations: you can declare an ObjectId using the dynamic keyword in C#, and you can then choose to access any of the properties or methods exposed by the object possessing that ID directly from the ObjectId itself. The ObjectId then takes care of the open & close operations implicitly, so there’s no need to mess around with transactions, etc.
If you’re interested in more background to this, I suggest taking a look at Albert Szilvasy’s class from AU 2009, where he gave us a hint of what was to come (although it was far from being a certainty, at that time – we have since found it to be a popular option via our annual API wishlist surveys).
OK, so what does this mean, in practice? Here’s some statically-typed C# code that iterates through the layer table and prefixes all layers (other than layer “0”) with a string:
public static void ChangeLayerNames()
{
  Database db = HostApplicationServices.WorkingDatabase;
  using (Transaction tr = db.TransactionManager.StartTransaction())
  {
    LayerTable lt =
      (LayerTable)tr.GetObject(
        db.LayerTableId, OpenMode.ForRead
      );
    foreach (ObjectId ltrId in lt)
    {
      LayerTableRecord ltr =
        (LayerTableRecord)tr.GetObject(ltrId, OpenMode.ForRead);
      if (ltr.Name != "0")
      {
        ltr.UpgradeOpen();
        ltr.Name = "First Floor " + ltr.Name;
      }
    }
    tr.Commit();
  }
}
Here’s how the code could look if using dynamic types. I’ve done my best not to exaggerate the differences in numbers of lines by using different conventions in the two implementations, but I did have to add more line breaks in the above code to have it fit the width of this blog.
public static void ChangeLayerNamesDynamically()
{
  dynamic layers =
    HostApplicationServices.WorkingDatabase.LayerTableId;
  foreach (dynamic l in layers)
    if (l.Name != "0")
      l.Name = "First Floor " + l.Name;
}
Something that needs to be stressed: as the properties and methods are effectively being determined and then accessed/called at run-time, there is no IntelliSense available when coding dynamically (something Python and Ruby developers are used to, but we statically-typed .NET developers are probably less so). It would not be impossible to implement, but it would be very difficult: aside from types being inferred at the time code is being written (I’ll use the term develop-time, although I haven’t seen it used before), there would need to be some work done to effectively populate the list of properties & methods, which would typically mean opening the object and reflecting over its protocol… not at all straightforward to implement, as far as I can tell, especially when you don’t actually have AutoCAD running to help with accessing the objects.
A lack of IntelliSense does mean this style of coding it likely to be less accessible to novice coders, unless a more integrated REPL environment becomes available (as this is something that tends to balance out the lack of other develop-time tooling when scripting – just as people have found with the ability to execute fragments of AutoLISP at AutoCAD’s command-line, over the years).
I can see a couple of ways a REPL might end up being implemented. One would be to host the DLR and (say) the IronPython scripting engine (just as Albert showed during his above-mentioned AU class, as I did at my own AU 2009 class, and we’ve seen previously on this blog). Another would be to wait for the fruits of the future .NET Framework feature codenamed Roslyn, which enables Compiler as a Service for .NET  code, as this seems it would be a likely way to execute C# and VB.NET in a more dynamic way.
But that’s all for the future, and is very much my own speculation, at this stage.
One question that has come up in relation to “Dynamic .NET” in AutoCAD is around performance: as we’re effectively opening and closing an object every time we execute a method (and we may actually open and close an object twice – once for read, once for write – when we read one of its properties, modify it and store it back), isn’t there a performance impact? I admit I haven’t performed extensive benchmarking of this, and there is likely to be some overhead if dealing with lots of properties and methods. But in terms of getting something working quickly – which you can then tune for performance later, as needed – this is likely to be a valuable tool for developers, irrespective of the performance impact (which I’d hope to be modest).
And it really comes into its own when used in combination with LINQ. Here’s some code that Stephen Preston put together to test out some of the possibilities with LINQ (I’ve included the CLN and CLN2 commands – shown above – for completeness).
  1. using Autodesk.AutoCAD.Runtime;
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.DatabaseServices;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.EditorInput;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. namespace DynamicTyping
  9. {
  10.   public class Commands
  11.   {
  12.     [CommandMethod("CLN")]
  13.     public static void ChangeLayerNames()
  14.     {
  15.       Database db = HostApplicationServices.WorkingDatabase;
  16.       using (
  17.         Transaction tr = db.TransactionManager.StartTransaction()
  18.       )
  19.       {
  20.         LayerTable lt =
  21.           (LayerTable)tr.GetObject(
  22.             db.LayerTableId, OpenMode.ForRead
  23.           );
  24.         foreach (ObjectId ltrId in lt)
  25.         {
  26.           LayerTableRecord ltr =
  27.             (LayerTableRecord)tr.GetObject(ltrId, OpenMode.ForRead);
  28.           if (ltr.Name != "0")
  29.           {
  30.             ltr.UpgradeOpen();
  31.             ltr.Name = "First Floor " + ltr.Name;
  32.           }
  33.         }
  34.         tr.Commit();
  35.       }
  36.     }
  37.     [CommandMethod("CLN2")]
  38.     public static void ChangeLayerNamesDynamically()
  39.     {
  40.       dynamic layers =
  41.         HostApplicationServices.WorkingDatabase.LayerTableId;
  42.       foreach (dynamic l in layers)
  43.         if (l.Name != "0")
  44.           l.Name = "First Floor " + l.Name;
  45.     }
  46.     // Adds a custom dictionary in the extension dictionary of
  47.     // selected objects. Uses dynamic capabilities, but not LINQ.
  48.     [CommandMethod("AddMyDict")]
  49.     public void AddMyDict()
  50.     {
  51.       Document doc = Application.DocumentManager.MdiActiveDocument;
  52.       Database db = doc.Database;
  53.       Editor ed = doc.Editor;
  54.       PromptSelectionResult res = ed.GetSelection();
  55.       if (res.Status != PromptStatus.OK)
  56.         return;
  57.       foreach (dynamic ent in res.Value.GetObjectIds())
  58.       {
  59.         dynamic dictId = ent.ExtensionDictionary;
  60.         if (dictId == ObjectId.Null)
  61.         {
  62.           ent.CreateExtensionDictionary();
  63.           dictId = ent.ExtensionDictionary();
  64.         }
  65.         if (!dictId.Contains("MyDict"))
  66.           dictId.SetAt("MyDict", new DBDictionary());
  67.       }
  68.     }
  69.     // Adds a custom dictionary in the extension dictionary of
  70.     // selected objects, but this time using dynamic capabilities
  71.     // with LINQ. Conceptually simpler, but with some performance
  72.     // overhead, as we're using two queries: one to get entities
  73.     // without extension dictionaries (and then add them) and the
  74.     // other to get entities with extension dictionaries.
  75.     [CommandMethod("AddMyDict2")]
  76.     public void AddMyDict2()
  77.     {
  78.       PromptSelectionResult res =
  79.         Application.DocumentManager.MdiActiveDocument.Editor.
  80.           GetSelection();
  81.       if (res.Status != PromptStatus.OK)
  82.         return;
  83.       // Query for ents in selset without ExtensionDictionaries
  84.       var noExtDicts =
  85.         from ent in res.Value.GetObjectIds().Cast()
  86.           where ent.ExtensionDictionary == ObjectId.Null
  87.           select ent;
  88.       // Add extension dictionaries
  89.       foreach (dynamic ent in noExtDicts)
  90.         ent.CreateExtensionDictionary();
  91.       // Now we've added the ext dicts, we add our dict to each
  92.       var noMyDicts =
  93.         from ent in res.Value.GetObjectIds().Cast()
  94.           where !ent.ExtensionDictionary.Contains("MyDict")
  95.           select ent.ExtensionDictionary;
  96.       foreach (dynamic dict in noMyDicts)
  97.         dict.SetAt("MyDict", new DBDictionary());
  98.     }
  99.     // Access various bits of information using LINQ
  100.     [CommandMethod("IUL")]
  101.     public void InfoUsingLINQ()
  102.     {
  103.       Document doc = Application.DocumentManager.MdiActiveDocument;
  104.       Database db = doc.Database;
  105.       Editor ed = doc.Editor;
  106.       dynamic bt = db.BlockTableId;
  107.       // Dynamic .NET loop iteration
  108.       ed.WriteMessage("\n*** BlockTableRecords in this DWG ***");
  109.       foreach (dynamic btr in bt)
  110.         ed.WriteMessage("\n" + btr.Name);
  111.       // LINQ query - returns startpoints of all lines
  112.       ed.WriteMessage(
  113.         "\n\n*** StartPoints of Lines in ModelSpace ***"
  114.       );
  115.       dynamic ms = SymbolUtilityServices.GetBlockModelSpaceId(db);
  116.       var lineStartPoints =
  117.         from ent in (IEnumerable)ms
  118.           where ent.IsKindOf(typeof(Line))
  119.           select ent.StartPoint;
  120.       foreach (Point3d start in lineStartPoints)
  121.         ed.WriteMessage("\n" + start.ToString());
  122.       // LINQ query - all entities on layer '0'
  123.       ed.WriteMessage("\n\n*** Entities on Layer 0 ***");
  124.       var entsOnLayer0 =
  125.         from ent in (IEnumerable)ms
  126.           where ent.Layer == "0"
  127.           select ent;
  128.       foreach (dynamic e in entsOnLayer0)
  129.         ed.WriteMessage(
  130.           "\nHandle=" + e.Handle.ToString() + ", ObjectId=" +
  131.           ((ObjectId)e).ToString() + ", Class=" + e.ToString()
  132.         );
  133.       ed.WriteMessage("\n\n");
  134.       // Using LINQ with selection sets
  135.       PromptSelectionResult res = ed.GetSelection();
  136.       if (res.Status != PromptStatus.OK)
  137.         return;
  138.       // Select all entities in selection set that have an object
  139.       // called "MyDict" in their extension dictionary
  140.       var extDicts =
  141.         from ent in res.Value.GetObjectIds().Cast()
  142.           where ent.ExtensionDictionary != ObjectId.Null &&
  143.             ent.ExtensionDictionary.Contains("MyDict")
  144.           select ent.ExtensionDictionary.Item("MyDict");
  145.       // Erase our dictionary
  146.       foreach (dynamic myDict in extDicts)
  147.         myDict.Erase();
  148.     }
  149.   }
  150. }

You’ll see that certain operations become very straightforward when combining Dynamic .NET and LINQ. This could well be the “sweet spot” of the dynamic capability – it certainly makes LINQ a more natural choice when accessing various types of data in AutoCAD drawings.
That’s it for today’s post. Next time we’ll summarize the remaining API enhancements coming in AutoCAD 2013, before looking at the migration steps needed for .NET applications.
回复

使用道具 举报

2

主题

14

帖子

3

银币

初来乍到

Rank: 1

铜币
22
发表于 2012-7-21 18:09:00 | 显示全部楼层
AutoCAD.NET API对.NET框架的动态语言运行时的支持
回复

使用道具 举报

17

主题

69

帖子

2

银币

初露锋芒

Rank: 3Rank: 3Rank: 3

铜币
137
发表于 2012-7-21 22:35:00 | 显示全部楼层
飞狐老大的帖子,一定要支持!
回复

使用道具 举报

13

主题

84

帖子

6

银币

初露锋芒

Rank: 3Rank: 3Rank: 3

铜币
136
发表于 2012-7-22 00:10:00 | 显示全部楼层

ObjectId对动态语言的支持,就是VB.NET早先的后期绑定。
实际使用起来并不方便,IDE没有智能提示,属性方法都要是手动输入。
本来DBObject可以直接实现的功能,非要绕弯用ObjectId实现,好像为动态而动态,不知道Autodesk怎么想的。
回复

使用道具 举报

72

主题

2726

帖子

9

银币

社区元老

Rank: 75Rank: 75Rank: 75

铜币
3014
发表于 2012-7-22 09:40:00 | 显示全部楼层
只是看起来很Cool而已 呵呵
回复

使用道具 举报

51

主题

70

帖子

5

银币

后起之秀

Rank: 20Rank: 20Rank: 20Rank: 20

铜币
274
发表于 2012-10-15 01:13:00 | 显示全部楼层
很好很强大
回复

使用道具 举报

10

主题

45

帖子

4

银币

初露锋芒

Rank: 3Rank: 3Rank: 3

铜币
85
发表于 2012-10-15 21:12:00 | 显示全部楼层
飞狐哥哥又回来了!
回复

使用道具 举报

3

主题

18

帖子

1

银币

初来乍到

Rank: 1

铜币
30
发表于 2012-10-17 17:24:00 | 显示全部楼层
为什么谈VB.NET的这么少呢?
回复

使用道具 举报

发表回复

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

  • 微信公众平台

  • 扫描访问手机版

  • 点击图片下载手机App

QQ|关于我们|小黑屋|乐筑天下 繁体中文

GMT+8, 2025-3-14 21:10 , Processed in 0.366079 second(s), 68 queries .

© 2020-2025 乐筑天下

联系客服 关注微信 帮助中心 下载APP 返回顶部 返回列表