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

0 Members and 1 Guest are viewing this topic.

Jeff H

  • Needs a day job
  • Posts: 5962
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: 92
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: 11653
  • 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?"
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate to theSwamp<--

kaefer

  • Swamp Rat
  • Posts: 572
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: 819
  • 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.

nullptr

  • BricsCAD
  • Needs a day job
  • Posts: 6756
  • 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: 304
  • 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

  • Swamp Rat
  • Posts: 572
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: 819
  • 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: 5962
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

  • Swamp Rat
  • Posts: 572
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 »

TheMaster

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #15 on: April 06, 2012, 08:27:45 AM »
Using the code from the posted link:

Code - C#: [Select]
  1.  
  2. namespace Namespace1
  3. {
  4.  
  5.  public static class PropertyAccessTest
  6.  {
  7.    public static void Run()
  8.    {
  9.      var nTimes = 2500000;
  10.      var value = "Hello World";
  11.      var myTestType = new MyObject();
  12.      DateTime start, end;
  13.  
  14.      var property = typeof(MyObject).GetProperty("MyProperty");
  15.  
  16.      Stopwatch sw = new Stopwatch();
  17.      sw.Start();
  18.      for (var i = 0; i < nTimes; i++)
  19.      {
  20.        property.SetValue(myTestType, value, null);
  21.      }
  22.      sw.Stop();
  23.      Console.WriteLine("Straight reflection: PropertyInfo.SetValue(): {0} ms", sw.ElapsedMilliseconds);
  24.  
  25.      dynamic myTestType2 = new MyObject();
  26.      sw.Reset();
  27.      sw.Start();
  28.      for (int i = 0; i < nTimes; i++)
  29.      {
  30.        myTestType2.MyProperty = value;
  31.      }
  32.      sw.Stop();
  33.      Console.WriteLine("System.Dynamic: dynamic.MyProperty = value: {0} ms", sw.ElapsedMilliseconds);
  34.  
  35.      var myObject3 = new MyObject();
  36.      sw.Reset();
  37.      sw.Start();
  38.      for (int i = 0; i < nTimes; i++)
  39.      {
  40.        myObject3.MyProperty = value;
  41.      }
  42.      sw.Stop();
  43.      Console.WriteLine("Direct Code: MyObject.MyProperty = value: {0} ms", sw.ElapsedMilliseconds);
  44.  
  45.      // Given an instance of an object, and a string
  46.      // identifying a property of the object:
  47.  
  48.      FastProperty fastProp = new FastProperty( myObject3, "MyProperty" );
  49.  
  50.      // It's important to note that the instance of fastProp
  51.      // is both type- and member-specific, so it must be cached
  52.      // and keyed to both the type of the instance, and the
  53.      // property name, using some kind of caching scheme (which
  54.      // is merely an implementation detail):
  55.  
  56.      sw.Reset();
  57.      sw.Start();
  58.      for (int i = 0; i < nTimes; i++)
  59.      {
  60.        fastProp.Set( myObject3, value );
  61.      }
  62.      sw.Stop();
  63.      Console.WriteLine("FastProperty: fastProp.Set(myObject3, value): {0} ms", sw.ElapsedMilliseconds);
  64.    }
  65.  
  66.    public class MyObject
  67.    {
  68.      public string MyProperty { get; set; }
  69.    }
  70.  
  71.  }
  72.  
  73.  public class FastProperty
  74.  {
  75.    public PropertyInfo Property { get; set; }
  76.  
  77.    public Func<object, object> GetDelegate;
  78.    public Action<object, object> SetDelegate;
  79.  
  80.    public FastProperty( object instance, string property )
  81.      : this( instance.GetType().GetProperty( property ) )
  82.    {
  83.    }
  84.  
  85.    public FastProperty(PropertyInfo property)
  86.    {
  87.      this.Property = property;
  88.      InitializeGet();
  89.      InitializeSet();
  90.    }
  91.  
  92.    private void InitializeSet()
  93.    {
  94.      var instance = Expression.Parameter(typeof(object), "instance");
  95.      var value = Expression.Parameter(typeof(object), "value");
  96.  
  97.      // value as T is slightly faster than (T)value, so if it's not a value type, use that
  98.      UnaryExpression instanceCast = (!this.Property.DeclaringType.IsValueType) ? Expression.TypeAs(instance, this.Property.DeclaringType) : Expression.Convert(instance, this.Property.DeclaringType);
  99.      UnaryExpression valueCast = (!this.Property.PropertyType.IsValueType) ? Expression.TypeAs(value, this.Property.PropertyType) : Expression.Convert(value, this.Property.PropertyType);
  100.      this.SetDelegate = Expression.Lambda<Action<object, object>>(Expression.Call(instanceCast, this.Property.GetSetMethod(), valueCast), new ParameterExpression[] { instance, value }).Compile();
  101.    }
  102.  
  103.    private void InitializeGet()
  104.    {
  105.      var instance = Expression.Parameter(typeof(object), "instance");
  106.      UnaryExpression instanceCast = (!this.Property.DeclaringType.IsValueType) ? Expression.TypeAs(instance, this.Property.DeclaringType) : Expression.Convert(instance, this.Property.DeclaringType);
  107.      this.GetDelegate = Expression.Lambda<Func<object, object>>(Expression.TypeAs(Expression.Call(instanceCast, this.Property.GetGetMethod()), typeof(object)), instance).Compile();
  108.    }
  109.  
  110.    public object Get(object instance)
  111.    {
  112.      return this.GetDelegate(instance);
  113.    }
  114.  
  115.    public void Set(object instance, object value)
  116.    {
  117.      this.SetDelegate(instance, value);
  118.    }
  119.  }
  120.  
  121. }
  122.  
  123.  

My results (this was run on a netbook):

Code: [Select]

Straight reflection: PropertyInfo.SetValue(): 5950 ms
System.Dynamic: dynamic.MyProperty = value: 145 ms
Direct Code: MyObject.MyProperty = value: 9 ms
FastProperty: fastProp.Set(myObject3, value): 170 ms




kaefer

  • Swamp Rat
  • Posts: 572
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #16 on: April 07, 2012, 12:47:11 PM »
Code: [Select]
Straight reflection: PropertyInfo.SetValue(): 5950 ms
System.Dynamic: dynamic.MyProperty = value: 145 ms
Direct Code: MyObject.MyProperty = value: 9 ms
FastProperty: fastProp.Set(myObject3, value): 170 ms

This is impressiv. Thanks for not pointing out that going by InvokeMember is slower still in the region of 12000 ms.

Not having yet seen any hints as to the implementation in AutoCAD 2013, I've tried to explore but one mechanism to access members dynamically. Your performance analysis lets me believe that in the best case, the cost of opening DBObjects transparently and accessing their properties will be 1-2 orders of magnitude slower. In the worst case, it could be more than 4, like I would experience it with my attempt at DynamicObject.

Incidentally, taking the mutability out of the FastProperty class makes it almost readable.

Code - F#: [Select]
  1. type FastProperty(propertyInfo : PropertyInfo) =
  2.  
  3.    let setDelegate =
  4.        let instance = Expression.Parameter(typeof<obj>, "instance")
  5.        let value = Expression.Parameter(typeof<obj>, "value")
  6.  
  7.        let instanceCast =
  8.            if propertyInfo.DeclaringType.IsValueType then Expression.Convert(instance, propertyInfo.DeclaringType)
  9.            else Expression.TypeAs(instance, propertyInfo.DeclaringType)
  10.        let valueCast =
  11.            if propertyInfo.PropertyType.IsValueType then Expression.Convert(value, propertyInfo.PropertyType)
  12.            else Expression.TypeAs(value, propertyInfo.PropertyType)
  13.        Expression.Lambda<System.Action<obj, obj>>(Expression.Call(instanceCast, propertyInfo.GetSetMethod(), valueCast), [| instance; value |]).Compile()
  14.  
  15.    let getDelegate =
  16.        let instance = Expression.Parameter(typeof<obj>, "instance")
  17.        let instanceCast =
  18.            if propertyInfo.DeclaringType.IsValueType then Expression.Convert(instance, propertyInfo.DeclaringType)
  19.            else Expression.TypeAs(instance, propertyInfo.DeclaringType)
  20.        Expression.Lambda<System.Func<obj, obj>>(Expression.TypeAs(Expression.Call(instanceCast, propertyInfo.GetGetMethod()), typeof<obj>), instance).Compile()
  21.  
  22.    new(instance, property) =
  23.        FastProperty(instance.GetType().GetProperty property)
  24.  
  25.    member __.Get instance =
  26.        getDelegate.Invoke instance
  27.  
  28.    member __.Set(instance, value) =
  29.        setDelegate.Invoke(instance, value)

TheMaster

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #17 on: April 08, 2012, 02:18:05 AM »
Actually, when running those tests I noticed a few issues with
that FastProperty class as posted. First, there is a needless cast
of the result of the getter's Expression.Call() and you can call
the FastProperty's GetDelegate() and SetDelegate() delegates
directly for even better performance.

The other issues are it doesn't deal correctly with read-only
or write-only properties, indexed properties, etc., and has a
few other minor issues.

I've been using something like it for a while (prior to .NET 3.5,
I used Reflection.Emit, but that's no longer needed in 3.5 or
later), along with custom TypeDescriptors, to make AutoCAD's
Data Extraction run between 10-20X faster (yes, that wasn't
a typo, it runs 10-20x faster, without exaggeration).

Code: [Select]
Straight reflection: PropertyInfo.SetValue(): 5950 ms
System.Dynamic: dynamic.MyProperty = value: 145 ms
Direct Code: MyObject.MyProperty = value: 9 ms
FastProperty: fastProp.Set(myObject3, value): 170 ms

This is impressiv. Thanks for not pointing out that going by InvokeMember is slower still in the region of 12000 ms.

Not having yet seen any hints as to the implementation in AutoCAD 2013, I've tried to explore but one mechanism to access members dynamically. Your performance analysis lets me believe that in the best case, the cost of opening DBObjects transparently and accessing their properties will be 1-2 orders of magnitude slower. In the worst case, it could be more than 4, like I would experience it with my attempt at DynamicObject.


TheMaster

  • Guest
Re: “Dynamic .NET” transactions vs intellisense , type safety, etc....
« Reply #18 on: April 08, 2012, 09:07:54 AM »

Incidentally, taking the mutability out of the FastProperty class makes it almost readable.


LINQPad will show the equivalent C# code for any expression.

For example, LINQPad shows the lambda expression for the
FastProperty's property getter as:

Code - C#: [Select]
  1.  
  2.     // when the instance is  a value type:
  3.     instance => ((<Type>)instance).get_<PropertyName>() as object;
  4.  
  5.     // when the instance is a reference type:
  6.     instance => (instance as <Type>).get_<PropertyName>() as object;
  7.  
  8.  
« Last Edit: April 08, 2012, 09:14:33 AM by TheMaster »