Author Topic: “Dynamic .NET” transactions vs intellisense , type safety, etc....  (Read 11256 times)

0 Members and 1 Guest are viewing this topic.

Jeff H

  • Needs a day job
  • Posts: 6144
Quote
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.
Link
 
Just wondering your thoughts about using Dynamic Runtime Engine in 2013.
 
Not sure about performance but will not have to use transactions.
 
Although lose intellisense and type saftey and would need to pay attention to variable naming.
 
 
 
 

Chumplybum

  • Newt
  • Posts: 97
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #1 on: February 29, 2012, 08:54:05 PM »
as kean mentioned, i think it'll be good for those quick "i wonder what will happen if...." or "does this work if i do/change this?" routines, After which i think i'd go with the old school transaction way, but the examples using linq are very interesting.

Although, it would be handy to have a 'Dynamic .net' editor directly inside autocad, maybe as a palette... so its there on the side if you wanted to try something out. An idea for an app maybe???


Mark

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 11654
  • class keyThumper<T>:ILazy<T>
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #2 on: February 29, 2012, 09:50:40 PM »

I have a hotkey I use in forums and blogs

F? => "Is there any documentation for that?"
kdub, kdub_nz in other timelines.
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

kaefer

  • Guest
Link
 
Just wondering your thoughts about using Dynamic Runtime Engine in 2013.

There goes type safety down the drain.
Ar least they're implementing IEnumerable<T> on their collections.

Since a C# variable declared as dynamic is nothing else than System.Object with special capabilities, I would imagine accessing the DynEng from other CLR languages would not involve more than unboxing/casting the object in question. Hypothetical F#, with definition of the dynamic operator (?) implied:

Code - F#: [Select]
  1. let looseYourLines() =
  2.     SymbolUtilityServices.GetBlockModelSpaceId db :?> IEnumerable<obj>
  3.     |> Seq.filter (fun ent -> ent?IsKindOf(typeof<Line>))
  4.     |> Seq.iter (fun ent -> ent?Erase())

huiz

  • Swamp Rat
  • Posts: 913
  • Certified Prof C3D
I'm not sure this "dynamic .NET" is a big plus for the mean programmer. It is probably more confusing to read/debug code.

If they had asked me for suggestions, I would have had more interesting features which they could build in :-) But that is personal of course.

I hope they don't have changed that much that I need to recompile my applications just for use in 2013, and keep maintaining the same applications for older versions.
The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8661
  • AKA Daniel
One can also use the existing framework to add layers of abstraction to achieve a similar goal. I know it's cool to have 1001 ways to create a line though.  In the future will also have the new C++/Cx language for COM.. yes the old boy is making a comeback. In the end we will have this mushy DotNetNativeComLispDynamic thing.

No worries Kerry, it will be self documenting  :wink:

mohnston

  • Bull Frog
  • Posts: 305
  • CAD Programmer
I hope they don't have changed that much that I need to recompile my applications just for use in 2013, and keep maintaining the same applications for older versions.

It's the magical 3 year cycle and this one seems to be a doozy. Kean's other post (2013 AutoCAD console apps) which talks about redistributing the "core" functionality of the AutoCAD engine tells me you will need to recompile.
It's amazing what you can do when you don't know what you can't do.
CAD Programming Solutions

TheMaster

  • Guest
So, duck typing/late binding wasn't something Microsoft did to give C# language-parity with VB.NET's existing ability to late-bind to IDispatch members without the need for the messy GetType().InvokeMember(bla bla bla..) business? 

I think Kean may be overlooking a key benefit of dynamic (for C# programmers at least), which is:

Code: [Select]

    DBObject obj = ....

    dynamic acadObject = obj.AcadObject;


The acadObject variable should allow you to use any member of IAcadObject (or any other COM dual- interface supported by the IDispatch object), directly in code without the need for the .GetType().InvokeMember(...) nonsense, and is beneficial even for otherwise-strongly-typed code.


Quote
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.
Link
 
Just wondering your thoughts about using Dynamic Runtime Engine in 2013.
 
Not sure about performance but will not have to use transactions.
 
Although lose intellisense and type saftey and would need to pay attention to variable naming.

kaefer

  • Guest
Code: [Select]

    DBObject obj = ....

    dynamic acadObject = obj.AcadObject;


I do not get what the 2013 "Dynamic Engine" has to do with IDispatch or Interop. I'd like to think that almost all nooks and crannies of AutoCAD's API are reachable within managed code, and exceptions are few and far between.

Besides, deliberate loss of static typing can be had just now, only that there's no realistic way to forego the safety of transactions (with their implicit undo stack). E.g.:

Code - C#: [Select]
  1. Database db = HostApplicationServices.WorkingDatabase;
  2. TransactionManager tm = db.TransactionManager;
  3. using(Transaction tr = tm.StartTransaction())
  4. {
  5.     dynamic btr = SymbolUtilityServices.GetBlockModelSpaceId(db).GetObject(OpenMode.ForRead);
  6.     var ents =
  7.         from dynamic oid in (IEnumerable)btr
  8.         select oid.GetObject(OpenMode.ForRead) into ent
  9.         where ent is Line
  10.         select ent;
  11.     foreach (var ent in ents)
  12.     {
  13.         ent.UpgradeOpen();
  14.         ent.Erase();
  15.     }
  16.     tr.Commit();
  17. }

huiz

  • Swamp Rat
  • Posts: 913
  • Certified Prof C3D
I hope they don't have changed that much that I need to recompile my applications just for use in 2013, and keep maintaining the same applications for older versions.

It's the magical 3 year cycle and this one seems to be a doozy. Kean's other post (2013 AutoCAD console apps) which talks about redistributing the "core" functionality of the AutoCAD engine tells me you will need to recompile.

The magical 3 year cycle is not for .NET apps but 2013 indeed need new compiling.

http://through-the-interface.typepad.com/through_the_interface/2012/03/migrating-net-applications-to-work-with-autocad-2013.html

The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

TheMaster

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #10 on: March 02, 2012, 04:57:38 PM »

I do not get what the 2013 "Dynamic Engine" has to do with IDispatch or Interop. I'd like to think that almost all nooks and crannies of AutoCAD's API are reachable within managed code, and exceptions are few and far between.


Not sure what 'dynamic engine' you mean, but if you're talking about the dynamic ObjectId business, that's an implementation of the .NET equivalent of IDispatch. My comment was that all of the chatter about that seemed to overlook the more simple and obvious benefits of the dynamic keyword in C#, which allows late-bound calls to Automation objects (e.g, IDispatch) in the same way that has been possible to do in VB.NET, since the beginning (e.g., Dim someIDispatch As Object).

And yea, there's a few legit reasons why .NET programmers need to make calls to AutoCAD's COM API, just as some AutoCAD .NET API methods do.  If you look at how the DocumentCollection opens drawing files, you'll see an example.

Quote

Besides, deliberate loss of static typing can be had just now, only that there's no realistic way to forego the safety of transactions (with their implicit undo stack). E.g.:


AFAIK, transactions can rollback changes made to a database, regardless of how they are done (e.g., by directly opening and closing objects). As they relate to UNDO/REDO, transactions can be thought of as the programmatic interface to the UNDO Begin/End subcommands.

Regarding your example code, you might find that using ObjectId.ObjectClass.IsSubclassOf() is faster than opening every object and using the 'as' operator to filter out unwanted types.

« Last Edit: March 02, 2012, 06:28:10 PM by TheMaster »

Jeff H

  • Needs a day job
  • Posts: 6144
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #11 on: March 30, 2012, 05:41:19 PM »
Instead of posting in links below and messing up some good threads I thought I would put it here.
 
I was just wondering the performance difference of some optimized reusable code like examples in links below vs using DLR.
Not asking for bench testing just wondering if anyone has done some test and in general how it performed.
 
Iterating through block definitions by name
 
Code - C#: [Select]
  1.  
  2.         [CommandMethod("dynFil")]
  3.         public static void dynFil()
  4.         {            
  5.             dynamic bt = HostApplicationServices.WorkingDatabase.BlockTableId;
  6.             var block =
  7.               (from blk in (IEnumerable<dynamic>)bt
  8.               where blk.Name == "Sink"
  9.               select blk)
  10.               .First();
  11.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(block.Name);
  12.         }
  13.  
  14.  
  15.  

Quick & Easy Managed Wrapper Type Discovery from ObjectIds
 
Code - C#: [Select]
  1.  
  2.    [CommandMethod("dynCir")]
  3.         public static void dynCir()
  4.         {
  5.             dynamic ms = SymbolUtilityServices.GetBlockModelSpaceId(HostApplicationServices.WorkingDatabase);
  6.             var circles =
  7.               from ent in (IEnumerable<dynamic>)ms
  8.               where ent.IsKindOf(typeof(Circle))
  9.               select ent;
  10.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(circles.Count().ToString());
  11.         }
  12.  
  13.  

 
 edit:kdub - link repaired
 
« Last Edit: March 30, 2012, 06:10:29 PM by Kerry »

TheMaster

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #12 on: March 31, 2012, 12:09:49 AM »
Instead of posting in links below and messing up some good threads I thought I would put it here.
 
I was just wondering the performance difference of some optimized reusable code like examples in links below vs using DLR.
Not asking for bench testing just wondering if anyone has done some test and in general how it performed.
 
Iterating through block definitions by name
 
Code - C#: [Select]
  1.  
  2.         [CommandMethod("dynFil")]
  3.         public static void dynFil()
  4.         {            
  5.             dynamic bt = HostApplicationServices.WorkingDatabase.BlockTableId;
  6.             var block =
  7.               (from blk in (IEnumerable<dynamic>)bt
  8.               where blk.Name == "Sink"
  9.               select blk)
  10.               .First();
  11.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(block.Name);
  12.         }
  13.  
  14.  
  15.  

Quick & Easy Managed Wrapper Type Discovery from ObjectIds
 
Code - C#: [Select]
  1.  
  2.    [CommandMethod("dynCir")]
  3.         public static void dynCir()
  4.         {
  5.             dynamic ms = SymbolUtilityServices.GetBlockModelSpaceId(HostApplicationServices.WorkingDatabase);
  6.             var circles =
  7.               from ent in (IEnumerable<dynamic>)ms
  8.               where ent.IsKindOf(typeof(Circle))
  9.               select ent;
  10.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(circles.Count().ToString());
  11.         }
  12.  
  13.  
edit:kdub - link repaired

Well, if we discount using BlockTable.Has() or the indexer, then
I think you can write the first one more simply as:

Code - C#: [Select]
  1.  
  2.  dynamic bt = HostApplicationServices.WorkingDatabase.BlockTableId;
  3.  var block = ((IEnumerable<dynamic>) bt).FirstOrDefault( blk => blk.Name == "Sink" );
  4.  if( block != null )
  5.     Application....WriteMessage(" Found a Sink " );
  6.  
  7.  

Can't tell you whether dynamic ObjectId is faster, but I would not put my money on it.

If IsKindOf() is checking runtime classes then it has to look up the
RXClass for the managed wrapper type (or cache it somehow), and
compare that to the runtime class of the ObjectId, so I would guess
that it doesn't require opening the object to do that.

I'm still experimenting with IsTypeOf<T>() and the related code, and
may have found a few more optimizations.

kaefer

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #13 on: April 05, 2012, 10:08:58 AM »
I had a look into System.Dynamic.DynamicObject, with a view to establish some dynamic functionality in AutoCAD 2012 (or at most 2011, since those dynamics require .NET 4.0). Getting an iterator in a foreach loop from the dynamic object seems to work:

Code - C#: [Select]
  1. [CommandMethod("DeleteShortLinesDynamically")]
  2. public void DeleteShortLinesDynamicallyCmd()
  3. {
  4.     Database db = HostApplicationServices.WorkingDatabase;
  5.     TransactionManager tm = db.TransactionManager;
  6.     using (Transaction tr = tm.StartTransaction())
  7.     {
  8.         dynamic btr = new MyDynamicObject(SymbolUtilityServices.GetBlockModelSpaceId(db));
  9.         foreach (var ent in btr)
  10.         {
  11.             if (ent.IsKindOf(typeof(Line)) && ent.Length < 0.1)
  12.             {
  13.                 ent.UpgradeOpen();
  14.                 ent.Erase();
  15.             }
  16.         }
  17.         tr.Commit();
  18.     }
  19. }

What doesn't work is that none of those overriden methods are actually called when dealing with a Linq expression, neither in query syntax nor by extension methods. Maybe it's really necessary to implement IDynamicMetaObjectProvider to get around this apparent restriction. It's only that inheriting from DynamicObject seems so much simpler.

Here's my attempt at MyDynamicObject as demonstrated in the code above.
Code - C#: [Select]
  1. public class MyDynamicObject : DynamicObject
  2. {
  3.     public MyDynamicObject(ObjectId oid)
  4.     {
  5.         ObjectId = oid;
  6.     }
  7.     public ObjectId ObjectId { get; private set; }
  8.     private DBObject dbobject;
  9.     public DBObject DBObject
  10.     {
  11.         get
  12.         {
  13.             if (dbobject == null)
  14.                 dbobject = ObjectId.GetObject(OpenMode.ForRead);
  15.             return dbobject;
  16.         }
  17.     }
  18.     public override bool TryGetMember(GetMemberBinder binder, out object result)
  19.     {
  20.         try
  21.         {
  22.             object xxx = DBObject.GetType().InvokeMember(binder.Name, BindingFlags.GetProperty, null, DBObject, null);
  23.             result = xxx == null ? null : promoteId(xxx);
  24.             return true;
  25.         }
  26.         catch
  27.         {
  28.             result = null;
  29.             return false;
  30.         }
  31.     }
  32.     public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
  33.     {
  34.         try
  35.         {
  36.             object xxx = DBObject.GetType().InvokeMember(binder.Name, BindingFlags.InvokeMethod, null, DBObject, args);
  37.             result = xxx == null ? null : promoteId(xxx);
  38.             return true;
  39.         }
  40.         catch
  41.         {
  42.             result = null;
  43.             return false;
  44.         }
  45.     }
  46.     public override bool TrySetMember(SetMemberBinder binder, object value)
  47.     {
  48.         try
  49.         {
  50.             DBObject.GetType().InvokeMember(binder.Name, BindingFlags.SetProperty, null, DBObject, new object[] { value });
  51.             return true;
  52.         }
  53.         catch
  54.         {
  55.             return false;
  56.         }
  57.     }
  58.     public override bool TryConvert(ConvertBinder binder, out object result)
  59.     {
  60.         try
  61.         {
  62.             if (binder.ReturnType == typeof(IEnumerable))
  63.             {
  64.                 result = testIEnum(DBObject);
  65.             }
  66.             else if (binder.ReturnType == typeof(ObjectId))
  67.             {
  68.                 result = ObjectId;
  69.             }
  70.             else
  71.             {
  72.                 result = DBObject;
  73.             }
  74.             return true;
  75.         }
  76.         catch
  77.         {
  78.             result = null;
  79.             return false;
  80.         }
  81.     }
  82.     object promoteId(object value)
  83.     {
  84.         if (!(value is ObjectId))
  85.             return value;
  86.         return new MyDynamicObject((ObjectId)value);
  87.     }
  88.     static object testIEnum(object value)
  89.     {
  90.         if (!(value is AttributeCollection ||
  91.             value is ObjectIdCollection ||
  92.             value is SymbolTable ||
  93.             value is SymbolTableRecord ||
  94.             value is IEnumerable<ObjectId>))
  95.             return value;
  96.         return makeIEnum(value);
  97.     }
  98.     static IEnumerable<MyDynamicObject> makeIEnum(object value)
  99.     {
  100.         foreach (ObjectId oid in (IEnumerable)value)
  101.         {
  102.             yield return new MyDynamicObject(oid);
  103.         }
  104.     }
  105.     public bool IsKindOf(Type type)
  106.     {
  107.         return DBObject.GetRXClass().IsDerivedFrom(RXClass.GetClass(type));
  108.     }
  109. }

TheMaster

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #14 on: April 05, 2012, 07:51:46 PM »
Well, it looks like you're putting a wrapper around ObjectId.GetObject(),
that uses reflection to access members of the underlying DBObject.

Personally, I would want no part of anything that uses reflection
as it can be up to 500x slower than direct calls via code.

The only place where I think it may be useful is in a data-binding
context, where one is data binding to a property of a DBObject,
but where the datasource is or must be an ObjectId.  Other than
that, I don't see much point to dynamic, reflection-based access
directly via code, as it seems to serve only to eliminate the need
to open ObjectIds to get DBObjects, but at an absurdly-expensive
cost, in terms of performance.

If you really insist on pursuing something that slow for use in code
(or just data binding), then you can create and cache a delegate for
accessing a member the first time the member is accessed, and use
the cached delegate on subsequent accesses, which will run a few
hundred times faster than straight reflection.

https://msmvps.com/blogs/jon_skeet/archive/2008/08/09/making-reflection-fly-and-exploring-delegates.aspx

http://geekswithblogs.net/Madman/archive/2008/06/27/faster-reflection-using-expression-trees.aspx

I had a look into System.Dynamic.DynamicObject, with a view to establish some dynamic functionality in AutoCAD 2012 (or at most 2011, since those dynamics require .NET 4.0). Getting an iterator in a foreach loop from the dynamic object seems to work:
« Last Edit: April 05, 2012, 08:00:34 PM by TheMaster »