Author Topic: Quick & Easy Managed Wrapper Type Discovery from ObjectIds  (Read 1729 times)

0 Members and 1 Guest are viewing this topic.

TT

  • Swamp Rat
  • Posts: 826
Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« on: March 29, 2012, 07:53:40 pm »
Recently, I was investigating an issue in some code I wrote that processed large
arrays of ObjectIds, and was seeing a significant, un-explainable delay in my timings.

As it turned out, the culprit was Disposable.Wrapper's == operator overload:

Code - C#: [Select]
  1.  
  2.   RXClass rxclass = RXClass.GetClass( typeof( T ) );
  3.  
  4.   foreach( ObjectId id in collection )
  5.   {
  6.        if( id.ObjectClass == rxclass || id.ObjectClass.IsDerivedFrom( rxclass ) )
  7.            yield return id.GetObject(...);
  8.   }
  9.  

The comparison using == was leading to a boxing operation, because
the overload of DisposableWrapper's == operator calls its overload of
bool Equals( object ), where the boxing/unboxing occurs.

So, I've concocted a workaround that avoids calling DisposableWrapper's
== operator, and as it turns out, also makes it brain-dead simple to perform
ad-hoc managed wrapper type discovery against ObjectIds, without incurring
the overhead of having to lookup two RXClass objects.

With the included code, you can use the extension method IsTypeOf<T>()
on any ObjectId, to find out if the managed wrapper returned by opening
the ObjectId is an instance of the generic argument.

For example, to find out if a given ObjectId references an object that is
a subclass of the Curve managed wrapper type, you only need do this:

Code - C#: [Select]
  1.  
  2.    ObjectId id = GetSomeObjectId();
  3.  
  4.    if( id.IsTypeOf<Curve>() )
  5.      Console.WriteLine( "The ObjectId references a Curve object" );
  6.    else
  7.      Console.WriteLine( "The ObjectId does not reference a Curve object" );
  8.  
  9.    // Or a Circle:
  10.  
  11.    ObjectId id = GetSomeObjectId();
  12.  
  13.    if( id.IsTypeOf<Circle>() )
  14.       Console.WriteLine( "The id references a Circle");
  15.  

  The efficiency of IsTypeOf<T>() comes from two major factors:
 
  1. It compares RXClass instances by comparing the value of their
  UnmanagedObject property, (DisposableWrapper.Equals(object) does
  the same), but here it is done without a boxing operation as the
  DisposableWrapper's == operator does.
 
  2. The RXClass<T> class caches the value of the associated RXClass's
  UnmanagedObject pointer in a static member, so it doesn't have to
  be looked-up each time it compares the RXClass to the ObjectClass
  property of an ObjectId. Hence, to test an ObjectId's ObjectClass
  it doesn't need to call RXClass.GetClass(type) to get the runtime
  class for the managed wrapper type to compare it to the ObjectId's
  ObjectClass property value.

So, here it is:

Code - C#: [Select]
  1.  
  2.  public static AcRxExtensionMethods
  3.  {
  4.    // ObjectId.IsTypeOf<T>() extension method:
  5.    //
  6.    // returns true if the managed wrapper returned
  7.    // by opening the given ObjectId is an instance
  8.    // of the generic argument type or a derived type.
  9.    //
  10.    // Example: Tells if the DBObject referenced by
  11.    // the given ObjectId is an instance of a Curve:
  12.    //
  13.    //      ObjectId id = GetSomeObjectId();
  14.    //
  15.    //      bool isCurve = id.IsTypeOf<Curve>();
  16.    //
  17.    // The advantage in the above use of IsTypeOf<T>() is
  18.    // that it doesn't require the RXClass for Curve to be
  19.    // fetched on each call, because it is already cached
  20.    // in a static member of the generic RXClass<Curve> type.
  21.    //
  22.    // The other benefit is that IsTypeOf<T>() can be
  23.    // used anywhere, and without a transaction, since
  24.    // it doesn't need to open the database object.
  25.    //
  26.    // The same can also be done more directly,
  27.    // but less-intuitively:
  28.    //
  29.    //   bool isCurve = id.ObjectClass.IsAssignableTo<Curve>();
  30.    //
  31.    //
  32.    // And of course, without using any of these helper APIs,
  33.    // one would need to do:
  34.    //
  35.    //   bool isCurve = id.ObjectClass.IsDerivedFrom(
  36.    //                       RXClass.GetClass( typeof( Curve ) )
  37.    //                  );
  38.    //
  39.  
  40.    public static bool IsTypeOf<T>( this ObjectId id )
  41.      where T: DBObject
  42.    {
  43.      return id.ObjectClass.IsAssignableTo<T>();
  44.    }
  45.  
  46.    // Like IsTypeOf<T>, except ids whose opened managed wrapper
  47.    // type are derived from T, but are not T itself, do not match.
  48.    //
  49.    // Use this method when you require all ids to be instances
  50.    // of concrete types (e.g., BlockReference, DBText, etc.), and
  51.    // there is no chance that any objects will be derived from
  52.    // the given type. Using abstract types as the generic argument
  53.    // (e.g., Curve, Entity, DBObject, etc.) is not valid, as the
  54.    // result will always be false:
  55.    //
  56.    // This method is faster than IsTypeOf<T>.
  57.  
  58.    public static bool IsInstanceOf<T>( this ObjectId id )
  59.      where T: DBObject
  60.    {
  61.      return RXClass<T>.unmanagedObject == id.ObjectClass.UnmanagedObject;
  62.    }
  63.  
  64.    // returns true, if a managed wrapper with the given
  65.    // RXClass can be cast to a variable of type T, where
  66.    // T is DBObject or any derived type:
  67.  
  68.    public static bool IsAssignableTo<T>( this RXClass rxclass )
  69.      where T: DBObject
  70.    {
  71.      return rxclass.UnmanagedObject == RXClass<T>.unmanagedObject
  72.        || rxclass.IsDerivedFrom( RXClass<T>.instance );
  73.    }
  74.  
  75.    public static bool IsEqual<T>( this RXClass rxclass )
  76.      where T: DBObject
  77.    {
  78.      return rxclass.UnmanagedObject == RXClass<T>.unmanagedObject;
  79.    }
  80.  
  81.    // IEnumerable<ObjectId>.OfType<T>() Extension:
  82.    //
  83.    // The OfType<T> extension method is the plural form
  84.    // of IsTypeOf<T>, that reduces an IEnumerable<ObjectId>
  85.    // sequence to only those ids whose managed wrapper
  86.    // type is an instance of the generic argument type.
  87.    //
  88.    // Here we reduce an ObjectIdCollection to a sequence
  89.    // containing only the ObjectIds of Circle entities:
  90.    //
  91.    //   ObjectIdCollection entityIds = GetSomeObjectIdCollection();
  92.    //
  93.    //   var circleIds = entityIds.OfType<Circle>();
  94.  
  95.    // Can be invoked on ObjectId[] arrays or List<ObjectId>:
  96.  
  97.    public static IEnumerable<ObjectId> OfType<T>( this IEnumerable<ObjectId> src )
  98.      where T: DBObject
  99.    {
  100.      ObjectId[] array = source as ObjectId[];
  101.      if( array != null )
  102.        return OfTypeIterator( array );
  103.      else
  104.        return src.Where( IsTypeOf<T> );
  105.    }
  106.  
  107.    static IEnumerable<ObjectId> OfTypeIterator<T>( this IEnumerable ids )
  108.      where T: DBObject
  109.    {
  110.      foreach( ObjectId id in ids )
  111.      {
  112.        if( id.ObjectClass.IsAssignableTo<T>() )
  113.          yield return id;
  114.      }
  115.    }
  116.  
  117.    // Specialize the case for array and ObjectIdCollection
  118.    // sources, both of which can be iterated faster using
  119.    // their indexers, verses an IEnumerator<T>:
  120.  
  121.    static IEnumerable<ObjectId> OfTypeIterator<T>( this ObjectId[] ids )
  122.      where T: DBObject
  123.    {
  124.      int cnt = ids.Length;
  125.      for( int i = 0; i < cnt; i++ )
  126.      {
  127.        ObjectId id = ids[i];
  128.        if( id.ObjectClass.IsAssignableTo<T>() )
  129.          yield return id;
  130.      }
  131.    }
  132.  
  133.    static IEnumerable<ObjectId> OfTypeIterator<T>( this ObjectIdCollection ids )
  134.      where T: DBObject
  135.    {
  136.      int cnt = ids.Count;
  137.      for( int i = 0; i < cnt; i++ )
  138.      {
  139.        ObjectId id = ids[i];
  140.        if( id.ObjectClass.IsAssignableTo<T>() )
  141.          yield return id;
  142.      }
  143.    }
  144.  
  145.    // Overload for ObjectIdCollections:
  146.  
  147.    public static IEnumerable<ObjectId> OfType<T>( this ObjectIdCollection ids )
  148.      where T: DBObject
  149.    {
  150.      return OfTypeIterator<T>( ids );
  151.    }
  152.  
  153.    // Overload for BlockTableRecord:
  154.  
  155.    public static IEnumerable<ObjectId> OfType<T>( this BlockTableRecord btr, bool includingErased )
  156.      where T: DBObject
  157.    {
  158.      return OfTypeIterator<T>( includingErased ? btr.IncludingErased : btr );
  159.    }
  160.  
  161.    ////////////////////////////////////////////////////////////////////////////
  162.    // IEnumerable<ObjectId>.AreAnyOfType<T>():
  163.    //
  164.    // Return true if at least one ObjectId in the source references a
  165.    // database object whose opened managed wrapper type is an instance of T:
  166.  
  167.    public static bool AreAnyOfType<T>( this IEnumerable<ObjectId> ids )
  168.      where T: DBObject
  169.    {
  170.      return ids.Any( IsTypeOf<T> );
  171.    }
  172.  
  173.    public static bool AreAnyOfType<T>( this ObjectIdCollection ids )
  174.      where T: DBObject
  175.    {
  176.      int cnt = ids.Count;
  177.      for( int i = 0; i < cnt; i++ )
  178.      {
  179.        if( ids[i].IsTypeOf<T>() )
  180.          return true;
  181.      }
  182.      return false;
  183.    }
  184.  
  185.    ////////////////////////////////////////////////////////////////////////////
  186.    // IEnumerable<ObjectId>.AreAllOfType<T>():
  187.    //
  188.    // Return true if all ObjectIds in the source reference database
  189.    // objects whose opened managed wrapper type is an instance of T.
  190.  
  191.    public static bool AreAllOfType<T>( this IEnumerable<ObjectId> ids
  192.      where T: DBObject;
  193.    {
  194.      return ids.All( IsTypeOf<T> );
  195.    }
  196.  
  197.    public static bool AreAllOfType<T>( this ObjectIdCollection ids )
  198.      where T: DBObject;
  199.    {
  200.      int cnt = ids.Count;
  201.      for( int i = 0; i < cnt; i++ )
  202.      {
  203.        if( ! ids[i].IsTypeOf<T>() )
  204.          return false;
  205.      }
  206.      return true;
  207.    }  
  208.  
  209.    // Returns true if the given RXClass is the RXClass of the
  210.    // managed wrapper of type T:
  211.  
  212.    public static bool EqualsObjectClass<T>( this RXClass rxclass )
  213.      where T: DBObject;
  214.    {
  215.      return rxclass.UnmanagedObject == RXClass<T>.unmanagedObject;
  216.    }
  217.  
  218.  
  219.    // This caches the RXClass and value of its UnmanagedObject
  220.    // property for each managed wrapper type T, allowing extension
  221.    // methods of this class to avoid the boxing operation incurrred
  222.    // by using the DisposableWrapper's == operator.
  223.    //
  224.    // Caching the UnmanagedObject property value of each RXClass
  225.    // also exploits a limitation that precludes methods of types
  226.    // deriving from MarshalByRefObject from being inlined.
  227.    //
  228.    // This class handles the ModuleLoaded event so that in case a
  229.    // rebuild of the runtime class tree might invalidates cached
  230.    // RXClasses or the pointers to their native AcRxClass instance.
  231.  
  232.    static class RXClass<T> where T: DBObject
  233.    {
  234.      static RXClass()
  235.      {
  236.        SystemObjects.DynamicLinker.ModuleLoaded += moduleLoaded;
  237.      }
  238.  
  239.      public static RXClass instance = RXClass.GetClass( typeof( T ) );
  240.      public static IntPtr unmanagedObject = RXClass.GetClass( typeof( T ) ).UnmanagedObject;
  241.  
  242.      static void moduleLoaded( object sender, DynamicLinkerEventArgs e )
  243.      {
  244.        instance = RXClass.GetClass( typeof( T ) );
  245.        unmanagedObject = RXClass.GetClass( typeof( T ) ).UnmanagedObject;
  246.      }
  247.    }
  248.  }
  249.  
  250.  // This class can be used with a Dictionary<T, TValue>,
  251.  // List<T>, or HashSet<T>, etc., where 'T' is any type
  252.  // derived from DisposableWrapper. It compares objects
  253.  // faster than DisposableWrapper's == operator because
  254.  // the == operator entails a boxing operation.
  255.  
  256.  public class DisposableWrapperComparer : IEqualityComparer<DisposableWrapper>
  257.  {
  258.    // Faster than DisposableWrapper's == operator overload,
  259.    // because there's no boxing/unboxing (see implementation
  260.    // of DisposableWrapper.Equals(object), which is called by
  261.    // the DisposableWrapper's == and != operators):
  262.  
  263.    public bool Equals( DisposableWrapper a, DisposableWrapper b )
  264.    {
  265.      if( a == null )
  266.      {
  267.        return b == null;
  268.      }
  269.      return b != null ? a.UnmanagedObject == b.UnmanagedObject : false;
  270.    }
  271.  
  272.    public int GetHashCode( DisposableWrapper wrapper )
  273.    {
  274.      return wrapper.UnmanagedObject.ToInt32();
  275.    }
  276.  }
  277.  
  278.  

« Last Edit: March 30, 2012, 06:10:39 am by TheMaster »

TT

  • Swamp Rat
  • Posts: 826
Re: Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« Reply #1 on: March 31, 2012, 07:20:18 am »
I knew there was a major optimization that I was just not
seeing, and I think I've nailed it.

In the previously posted code, GetObjects<T>() must call
RXClass.IsDerivedFrom() on the ObjectClass property of
every single ObjectId in the sequence.

Well, doing that is pointless if there are no derived types
(for example, objects like BlockReference often have no
types derived from them, unless there are custom objects
present that derive from those types).

In this major revision, the code will monitor changes to the
ObjectARX runtime class tree, and will keep track of whether
each RXClass that's used with IsTypeOf<T>() (and its cousins)
have any child RXClasses.

In cases where the RXClass associated with the managed wrapper
type used as the generic argument has no derived RXClasses, the
code avoids calling IsDerivedFrom() on the ObjectClass of every
ObjectId in the sequence, resulting in a major performance gain.

Code - C#: [Select]
  1.  
  2.  public static class AcRxExtensionMethods
  3.  {
  4.    // ObjectId.IsTypeOf<T>() extension method:
  5.    //
  6.    // returns true if the managed wrapper returned
  7.    // by opening the given ObjectId is an instance
  8.    // of the generic argument type or a derived type.
  9.    //
  10.    // Example: Tells if the DBObject referenced by
  11.    // the given ObjectId is an instance of a Curve:
  12.    //
  13.    //   ObjectId id = GetSomeObjectId();
  14.    //
  15.    //   bool isCurve = id.IsTypeOf<Curve>();
  16.    //
  17.    // The advantage in the above use of IsTypeOf<T>() is
  18.    // that it doesn't require the RXClass for Curve to be
  19.    // fetched on each call, because it is already cached
  20.    // in a static member of the generic RXClass<Curve> type.
  21.    //
  22.    // The other benefit is that IsTypeOf<T>() can be
  23.    // used anywhere, and without a transaction, since
  24.    // it doesn't need to open the database object.
  25.    //
  26.    // The same can also be done more directly,
  27.    // but less-intuitively:
  28.    //
  29.    //   bool isCurve = id.ObjectClass.IsAssignableTo<Curve>();
  30.    //
  31.    //
  32.    // And of course, without using any of these helper APIs,
  33.    // one would need to do:
  34.    //
  35.    //   bool isCurve = id.ObjectClass.IsDerivedFrom(
  36.    //                       RXClass.GetClass( typeof( Curve ) )
  37.    //                  );
  38.    //
  39.  
  40.    public static bool IsTypeOf<T>( this ObjectId id )
  41.      where T: DBObject
  42.    {
  43.      return RXClass<T>.IsAssignableFrom( id.ObjectClass );
  44.    }
  45.  
  46.    // Like IsTypeOf<T>, except ids whose opened managed wrapper
  47.    // type are derived from T, but are not T itself, do not match.
  48.    //
  49.    // Use this method when you require all ids to be instances
  50.    // of concrete types (e.g., BlockReference, DBText, etc.), and
  51.    // there is no chance that any objects will be derived from
  52.    // the given type. Using abstract types as the generic argument
  53.    // (e.g., Curve, Entity, DBObject, etc.) is not valid, as the
  54.    // result will always be false:
  55.    //
  56.    // This method is faster than IsTypeOf<T>.
  57.  
  58.    public static bool IsInstanceOf<T>( this ObjectId id )
  59.      where T: DBObject
  60.    {
  61.      return RXClass<T>.unmanagedObject == id.ObjectClass.UnmanagedObject;
  62.    }
  63.  
  64.    // returns true, if a managed wrapper with the given
  65.    // RXClass can be cast to a variable of type T, where
  66.    // T is DBObject or any derived type:
  67.  
  68.    public static bool IsAssignableTo<T>( this RXClass rxclass )
  69.      where T: DBObject
  70.    {
  71.      return RXClass<T>.IsAssignableFrom( rxclass );
  72.    }
  73.  
  74.    // IEnumerable<ObjectId>.OfType<T>() Extension:
  75.    //
  76.    // The OfType<T> extension method is the plural form
  77.    // of IsTypeOf<T>, that reduces an IEnumerable<ObjectId>
  78.    // sequence to only those ids whose managed wrapper
  79.    // type is an instance of the generic argument type.
  80.    //
  81.    // Here we reduce an ObjectIdCollection to a sequence
  82.    // containing only the ObjectIds of Circle entities:
  83.    //
  84.    //   ObjectIdCollection entityIds = GetSomeObjectIdCollection();
  85.    //
  86.    //   var circleIds = entityIds.OfType<Circle>();
  87.  
  88.    // Can be invoked on ObjectId[] arrays or List<ObjectId>:
  89.  
  90.    public static IEnumerable<ObjectId> OfType<T>( this IEnumerable<ObjectId> source )
  91.      where T: DBObject
  92.    {
  93.      ObjectId[] array = source as ObjectId[];
  94.      if( array != null )
  95.        return OfTypeIterator( array );
  96.      else
  97.        return src.Where( IsTypeOf<T> );
  98.    }
  99.  
  100.    // Overload for ObjectIdCollections:
  101.  
  102.    public static IEnumerable<ObjectId> OfType<T>( this ObjectIdCollection ids )
  103.      where T: DBObject
  104.    {
  105.      return OfTypeIterator<T>( ids );
  106.    }
  107.  
  108.    // Overload for BlockTableRecord:
  109.  
  110.    public static IEnumerable<ObjectId> OfType<T>( this BlockTableRecord btr, bool includingErased )
  111.      where T: DBObject
  112.    {
  113.      return OfTypeIterator<T>( includingErased ? btr.IncludingErased : btr );
  114.    }
  115.  
  116.    static IEnumerable<ObjectId> OfTypeIterator<T>( this IEnumerable ids )
  117.      where T: DBObject
  118.    {
  119.      foreach( ObjectId id in ids )
  120.      {
  121.        if( RXClass<T>.IsAssignableFrom( id.ObjectClass )
  122.          yield return id;
  123.      }
  124.    }
  125.  
  126.    // Specialize the case for array and ObjectIdCollection
  127.    // sources, both of which can be iterated faster using
  128.    // their indexers, verses their IEnumerator:
  129.  
  130.    static IEnumerable<ObjectId> OfTypeIterator<T>( this ObjectId[] ids )
  131.      where T: DBObject
  132.    {
  133.      int cnt = ids.Length;
  134.      for( int i = 0; i < cnt; i++ )
  135.      {
  136.        ObjectId id = ids[i];
  137.        if( RXClass<T>.IsAssignableFrom( id.ObjectClass ) )
  138.          yield return id;
  139.      }
  140.    }
  141.  
  142.    static IEnumerable<ObjectId> OfTypeIterator<T>( this ObjectIdCollection ids )
  143.      where T: DBObject
  144.    {
  145.      int cnt = ids.Count;
  146.      for( int i = 0; i < cnt; i++ )
  147.      {
  148.        ObjectId id = ids[i];
  149.        if( RXClass<T>.IsAssignableFrom( id.ObjectClass ) )
  150.          yield return id;
  151.      }
  152.    }
  153.  
  154.    ////////////////////////////////////////////////////////////////////////////
  155.    // IEnumerable<ObjectId>.AreAnyOfType<T>():
  156.    //
  157.    // Return true if at least one ObjectId in the source references a
  158.    // database object whose opened managed wrapper type is an instance of T:
  159.  
  160.    public static bool AreAnyOfType<T>( this IEnumerable<ObjectId> ids )
  161.      where T: DBObject
  162.    {
  163.      return ids.Any( IsTypeOf<T> );
  164.    }
  165.  
  166.    public static bool AreAnyOfType<T>( this ObjectIdCollection ids )
  167.      where T: DBObject
  168.    {
  169.      int cnt = ids.Count;
  170.      for( int i = 0; i < cnt; i++ )
  171.      {
  172.        if( ids[i].IsTypeOf<T>() )
  173.          return true;
  174.      }
  175.      return false;
  176.    }
  177.  
  178.    ////////////////////////////////////////////////////////////////////////////
  179.    // IEnumerable<ObjectId>.AreAllOfType<T>():
  180.    //
  181.    // Return true if all ObjectIds in the source reference database
  182.    // objects whose opened managed wrapper type is an instance of T.
  183.    //
  184.    // This method is useful for validating the contents of
  185.    // an ObjectIdCollection or array of Object[], passed as
  186.    // an argument to an API.
  187.    //
  188.  
  189.    public static bool AreAllOfType<T>( this IEnumerable<ObjectId> ids
  190.      where T: DBObject;
  191.    {
  192.      return ids.All( IsTypeOf<T> );
  193.    }
  194.  
  195.    public static bool AreAllOfType<T>( this ObjectIdCollection ids )
  196.      where T: DBObject;
  197.    {
  198.      int cnt = ids.Count;
  199.      for( int i = 0; i < cnt; i++ )
  200.      {
  201.        if( ! ids[i].IsTypeOf<T>() )
  202.          return false;
  203.      }
  204.      return true;
  205.    }  
  206.  
  207.    // Returns true if the given RXClass is the RXClass of the
  208.    // managed wrapper of type T:
  209.  
  210.    public static bool EqualsObjectClass<T>( this RXClass rxclass )
  211.      where T: DBObject;
  212.    {
  213.      return rxclass.UnmanagedObject == RXClass<T>.unmanagedObject;
  214.    }
  215.  
  216.    // RXClass<T> Class:
  217.    //
  218.    // RXClass<T> caches the RXClass and value of its UnmanagedObject
  219.    // property for each managed wrapper type T, allowing extension
  220.    // methods of this class to avoid the boxing operation incurrred
  221.    // by using the DisposableWrapper's == operator. A bool indicating
  222.    // if the RXClass has children (derived RXClasses) is cached and
  223.    // updated whenever the ObjectARX runtime class tree is rebuilt.
  224.    //
  225.    // This class handles the ModuleLoaded/unload events, so that it
  226.    // can update its hasChildren member whenever the ObjectARX runtime
  227.    // class hierarchy is rebuilt, which could result in the addition
  228.    // or deletion of child RXClasses. Tracking whether this RXClass has
  229.    // derived/child RXClasses allows us to avoid a pointless call to
  230.    // RXClass.IsDerivedFrom() on RXClasses we compare to this RXClass.
  231.    //
  232.    // Caching the UnmanagedObject property value of each RXClass
  233.    // also exploits a limitation that precludes methods of types
  234.    // deriving from MarshalByRefObject from being inlined.
  235.  
  236.    static class RXClass<T> where T: DBObject
  237.    {
  238.      public static RXClass instance;
  239.      public static IntPtr unmanagedObject;
  240.      public static bool hasChildren;
  241.  
  242.      static RXClass()
  243.      {
  244.        Initialize();
  245.        SystemObjects.DynamicLinker.ModuleLoaded += moduleLoadUnload;
  246.        SystemObjects.DynamicLinker.ModuleUnloaded += moduleLoadUnload;
  247.      }
  248.  
  249.      public static bool IsAssignableFrom( RXClass rxclass )
  250.      {
  251.        return rxclass.UnmanagedObject == unmanagedObject
  252.          || ( hasChildren && rxclass.IsDerivedFrom( instance ) );
  253.      }
  254.  
  255.      public static bool Equals( RXClass rxclass )
  256.      {
  257.        return rxclass != null && rxclass.UnmanagedObject == unmanagedObject;
  258.      }
  259.  
  260.      static void moduleLoadUnload( object sender, DynamicLinkerEventArgs e )
  261.      {
  262.        Initialize();
  263.      }
  264.  
  265.      // The cost of this is high, and will happen every time
  266.      // a module is loaded and unloaded, but the performance
  267.      // gain resulting from caching this data is significant
  268.      // and far outweighs the slight delay imposed whenever
  269.      // a module is loaded or unloaded. It should be pointed
  270.      // out that the above event handlers are not added until
  271.      // the first use of code that uses IsTypeOf<T>, for each
  272.      // distinct type used as the generic argument.
  273.  
  274.      static void Initialize()
  275.      {
  276.        instance = RXClass.GetClass( typeof( T ) );
  277.        unmanagedObject = instance.UnmanagedObject;
  278.        hasChildren = HasChildren();
  279.      }
  280.  
  281.  
  282.      // Returns a bool indicating if at least one RXClass is
  283.      // directly derived from the RXClass instance member.
  284.      //
  285.      // If the result is false, this RXCLass has no children
  286.      // (e.g., there are no RXClasses derived from this RXClass),
  287.      // and hence, there is no point to calling IsDerivedFrom()
  288.      // on any other RXClass to find out if it is derived from
  289.      // this RXClass.
  290.      //
  291.      // In most cases where GetObjects<T> is used, the generic
  292.      // argument type will be a concrete type of entity, like
  293.      // BlockReference, Polyline, etc. Unless there are custom
  294.      // objects that derive from those concrete types (which is
  295.      // unlikely), there will be no runtime classes that derive
  296.      // from the RXClass for the managed wrapper type used as
  297.      // the generic argument, and we can avoid the cost of a
  298.      // call to RXClass.IsDerivedFrom() on the ObjectClass of
  299.      // every ObjectId in the sequence.
  300.  
  301.      static bool HasChildren()
  302.      {
  303.        foreach( DictionaryEntry e in SystemObjects.ClassDictionary )
  304.        {
  305.          RXClass rxclass = e.Value as RXClass;
  306.          if( rxclass != null && Equals( rxclass.MyParent ) )
  307.            return true;
  308.        }
  309.        return false;
  310.      }  
  311.  }
  312.  
  313.  
« Last Edit: March 31, 2012, 10:49:51 pm by TheMaster »

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 10997
  • class keyThumper<T>:ILazy<T>
Re: Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« Reply #2 on: March 31, 2012, 09:33:03 am »

That's a lot to digest Tony.
Thank you for sharing.

Best Regards
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate<--

TT

  • Swamp Rat
  • Posts: 826
Re: Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« Reply #3 on: April 01, 2012, 03:53:25 am »

That's a lot to digest Tony.
Thank you for sharing.

Best Regards

It's being refactored again  :roll:

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 10997
  • class keyThumper<T>:ILazy<T>
Re: Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« Reply #4 on: April 01, 2012, 04:36:24 am »

It's being refactored again  :roll:

red, green, refactor ..
:D .. as one does !!
« Last Edit: April 01, 2012, 03:50:34 pm by Kerry »
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate<--

Jeff H

  • Needs a day job
  • Posts: 4765
Re: Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« Reply #5 on: April 05, 2012, 08:48:25 pm »
I meant to post this the other day but if anyone intrested this is OTB 2013 classes that were registered after startup.
 
ClassName -- ParentClassName
(or NULL which you could probably guess the only one that was Null was AcRxObject Parent)
 
Quote
Ac3PointAngleConstraint  --  AcAngleConstraint
AcAeEEMgrObj  --  AcDbObject
AcAngleConstraint  --  AcExplicitConstraint
AcAnnoBadge  --  AcGiDrawable
AcAp3DPrintingDialogParams  --  AcApHostDialogParams
AcApAnnoSelectScaleDialogParams  --  AcApHostDialogParams
AcApArxAppDialogParams  --  AcApHostDialogParams
AcApAuthBeginDialogParams  --  AcApHostDialogParams
AcApAutoCompleteItem  --  AcRxObject
AcApAutoCompleteItemCommand  --  AcApAutoCompleteItem
AcApAutoCompleteList  --  AcRxObject
AcApBlockPropertyTableDialogParams  --  AcApHostDialogParams
AcApBoundaryDialogParams  --  AcApHostDialogParams
AcApCloudFolderBrowseDialogParams  --  AcApHostDialogParams
AcApColorDialogParams  --  AcApHostDialogParams
AcApCreatePlotProgressDialogParams  --  AcApHostDialogParams
.....
.....
.....

Ran very quickly and did not notice it run and was printing to command line and .txt file using a StreamWriter.
Basiclly was HasChildren function below that just printed results instead of testing.
 
Great stuff Tony,
 
Attached full results.
 
 
 

TT

  • Swamp Rat
  • Posts: 826
Re: Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« Reply #6 on: April 06, 2012, 04:25:08 am »
I meant to post this the other day but if anyone intrested this is OTB 2013 classes that were registered after startup.
 
ClassName -- ParentClassName
(or NULL which you could probably guess the only one that was Null was AcRxObject Parent)
 
Quote
Ac3PointAngleConstraint  --  AcAngleConstraint
AcAeEEMgrObj  --  AcDbObject
AcAngleConstraint  --  AcExplicitConstraint
AcAnnoBadge  --  AcGiDrawable
AcAp3DPrintingDialogParams  --  AcApHostDialogParams
AcApAnnoSelectScaleDialogParams  --  AcApHostDialogParams
AcApArxAppDialogParams  --  AcApHostDialogParams
AcApAuthBeginDialogParams  --  AcApHostDialogParams
AcApAutoCompleteItem  --  AcRxObject
AcApAutoCompleteItemCommand  --  AcApAutoCompleteItem
AcApAutoCompleteList  --  AcRxObject
AcApBlockPropertyTableDialogParams  --  AcApHostDialogParams
AcApBoundaryDialogParams  --  AcApHostDialogParams
AcApCloudFolderBrowseDialogParams  --  AcApHostDialogParams
AcApColorDialogParams  --  AcApHostDialogParams
AcApCreatePlotProgressDialogParams  --  AcApHostDialogParams
.....
.....
.....

Ran very quickly and did not notice it run and was printing to command line and .txt file using a StreamWriter.
Basiclly was HasChildren function below that just printed results instead of testing.
 
Great stuff Tony,
 
Attached full results.

Thanks. I'll be posting a refactored version soon that 's a tad
faster and uses minimal event handling overhead.

Instead of having to do a linear search for each RXClass, the
revised version gets the set of RXClasses that are returned by
every RXClass' MyParent property. If an RXClass is not returned
as the value of any other RXClass' MyParent property, it has no
derived RXClasses. 

That entire set of 'parents' is stored in a HashSet<RXClass>
and only requires a simple Contains() lookup rather than
having to walk the entire RXClass tree:

Here's the relevant part of the code:

Code - C#: [Select]
  1.  
  2. namespace LinqToAutoCAD.Utilities
  3. {
  4.  
  5.  public static class RuntimeClassInfo
  6.  {
  7.    // The set of RXClass instances which other
  8.    // RXClass instances are derived from:
  9.  
  10.    static HashSet<IntPtr> parents = null;
  11.    static bool reset = false;
  12.  
  13.    // Public interface:
  14.  
  15.    // This returns true if at least one RXClass
  16.    // is derived from the given RXClass.
  17.  
  18.    public static bool HasChildren( this RXClass rxclass )
  19.    {
  20.      return Parents.Contains( rxclass.UnmanagedObject );
  21.    }
  22.  
  23.    // Returns true if a simple comparison of the given
  24.    // RXClass with the ObjectClass of an ObjectId can
  25.    // be done to test for a matching object type.
  26.    //
  27.    // A simple comoparison of the two RXClass objects is
  28.    // possible when the RXClass that all objects must be
  29.    // an instance of has no children (derived RXClasses).
  30.    //
  31.    // If this returns false, then IsDerivedFrom() must be
  32.    // called on the RXClass returned by the ObjectClass
  33.    // property of an ObjectId, passing in the RXClass that
  34.    // is passed to this API.
  35.  
  36.    public static bool UseExactMatch( this RXClass rxclass )
  37.    {
  38.      return ! Parents.Contains( rxclass.UnmanagedObject );
  39.    }
  40.  
  41.    // Equivalent for runtime class associated with the
  42.    // managed wrapper generic argument type:
  43.  
  44.    public static bool UseExactMatch<T>() where T: DBObject
  45.    {
  46.      return ! typeof( T ).IsAbstract && UseExactMatch( RXClass.GetClass( typeof( T ) ) );
  47.    }
  48.  
  49.    public static bool UseExactMatch( Type type )
  50.    {
  51.      if( ! typeof( DBObject ).IsAssignableFrom( type ) )
  52.        throw new ArgumentException("Requires a type derived from DBObject");
  53.  
  54.      return ! type.IsAbstract && UseExactMatch( RXClass.GetClass( type ) );
  55.    }
  56.  
  57.    // Handle this to be notified when the runtime
  58.    // class tree has been rebuilt:
  59.  
  60.    public static event EventHandler Invalidated = null;
  61.  
  62.    static RuntimeClassInfo()
  63.    {
  64.      SystemObjects.DynamicLinker.ModuleLoaded += moduleLoadUnload;
  65.      SystemObjects.DynamicLinker.ModuleUnloaded += moduleLoadUnload;
  66.    }
  67.  
  68.    static void moduleLoadUnload( object sender, DynamicLinkerEventArgs e )
  69.    {
  70.      this.parents = null;
  71.      this.reset = true;
  72.      if( this.Invalidated != null )
  73.      {
  74.        this.Invalidated( null, EventArgs.Empty );
  75.      }
  76.    }
  77.  
  78.    internal static HashSet<IntPtr> Parents
  79.    {
  80.      get
  81.      {
  82.        if( this.parents == null )
  83.        {
  84.          this.parents = this.GetAllParents();
  85.        }
  86.        return this.parents;
  87.      }
  88.    }
  89.  
  90.    // This code can survive module load/unload events
  91.    // that fire while iterating over the ClassDictionary.
  92.    //
  93.    // If that happens, the code will abort the iteration,
  94.    // discard the result, and start iterating over the
  95.    // ClassDictionary again, and will do that repeatedly
  96.    // until it has completed without being interrupted by
  97.    // a module load/unload event. This is a necessary evil,
  98.    // because a module load/unload event can result in the
  99.    // the runtime class tree being rebuilt, invalidating
  100.    // the result.
  101.  
  102.    static HashSet<IntPtr> GetAllParents()
  103.    {
  104.      HashSet<IntPtr> items = new HashSet<IntPtr>();
  105.      RXClass baseClass = RXClass.GetClass( typeof( DBObject ) ).MyParent;
  106.      this.reset = true;
  107.      while( this.reset )
  108.      {
  109.        this.reset = false;
  110.        items.Clear();
  111.        foreach( DictionaryEntry e in SystemObjects.ClassDictionary )
  112.        {
  113.          RXClass rxclass = (RXClass) e.Value;
  114.          if( rxclass.IsDerivedFrom( baseClass ) )
  115.          {
  116.            RXClass parent = rxclass.MyParent;
  117.            if( parent != null )
  118.            {
  119.              items.Add( parent.UnmanagedObject );
  120.            }
  121.            if( this.reset )  
  122.            {                
  123.              break;    
  124.            }
  125.          }
  126.        }
  127.      }
  128.      items.TrimExcess();
  129.      return items;
  130.    }
  131.  
  132.  }
  133.  
  134. }
  135.  
  136.  
  137.  

« Last Edit: April 06, 2012, 09:21:47 am by TheMaster »

Andrey Bushman

  • Swamp Rat
  • Posts: 592
  • CAD admin, programmer.
Re: Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« Reply #7 on: July 06, 2012, 08:22:42 am »
I meant to post this the other day but if anyone intrested this is OTB 2013 classes that were registered after startup.
 
ClassName -- ParentClassName
(or NULL which you could probably guess the only one that was Null was AcRxObject Parent)
 
Quote
Ac3PointAngleConstraint  --  AcAngleConstraint
AcAeEEMgrObj  --  AcDbObject
AcAngleConstraint  --  AcExplicitConstraint
AcAnnoBadge  --  AcGiDrawable
AcAp3DPrintingDialogParams  --  AcApHostDialogParams
AcApAnnoSelectScaleDialogParams  --  AcApHostDialogParams
AcApArxAppDialogParams  --  AcApHostDialogParams
AcApAuthBeginDialogParams  --  AcApHostDialogParams
AcApAutoCompleteItem  --  AcRxObject
AcApAutoCompleteItemCommand  --  AcApAutoCompleteItem
AcApAutoCompleteList  --  AcRxObject
AcApBlockPropertyTableDialogParams  --  AcApHostDialogParams
AcApBoundaryDialogParams  --  AcApHostDialogParams
AcApCloudFolderBrowseDialogParams  --  AcApHostDialogParams
AcApColorDialogParams  --  AcApHostDialogParams
AcApCreatePlotProgressDialogParams  --  AcApHostDialogParams
.....
.....
.....

Ran very quickly and did not notice it run and was printing to command line and .txt file using a StreamWriter.
Basiclly was HasChildren function below that just printed results instead of testing.
 
Great stuff Tony,
 
Attached full results.

Thanks. I'll be posting a refactored version soon that 's a tad
faster and uses minimal event handling overhead.

Instead of having to do a linear search for each RXClass, the
revised version gets the set of RXClasses that are returned by
every RXClass' MyParent property. If an RXClass is not returned
as the value of any other RXClass' MyParent property, it has no
derived RXClasses. 

That entire set of 'parents' is stored in a HashSet<RXClass>
and only requires a simple Contains() lookup rather than
having to walk the entire RXClass tree:

Here's the relevant part of the code:

Code - C#: [Select]
  1.  
  2. namespace LinqToAutoCAD.Utilities
  3. {
  4.  
  5.  public static class RuntimeClassInfo
  6.  {
  7.    // The set of RXClass instances which other
  8.    // RXClass instances are derived from:
  9.  
  10.    static HashSet<IntPtr> parents = null;
  11.    static bool reset = false;
  12.  
  13.    // Public interface:
  14.  
  15.    // This returns true if at least one RXClass
  16.    // is derived from the given RXClass.
  17.  
  18.    public static bool HasChildren( this RXClass rxclass )
  19.    {
  20.      return Parents.Contains( rxclass.UnmanagedObject );
  21.    }
  22.  
  23.    // Returns true if a simple comparison of the given
  24.    // RXClass with the ObjectClass of an ObjectId can
  25.    // be done to test for a matching object type.
  26.    //
  27.    // A simple comoparison of the two RXClass objects is
  28.    // possible when the RXClass that all objects must be
  29.    // an instance of has no children (derived RXClasses).
  30.    //
  31.    // If this returns false, then IsDerivedFrom() must be
  32.    // called on the RXClass returned by the ObjectClass
  33.    // property of an ObjectId, passing in the RXClass that
  34.    // is passed to this API.
  35.  
  36.    public static bool UseExactMatch( this RXClass rxclass )
  37.    {
  38.      return ! Parents.Contains( rxclass.UnmanagedObject );
  39.    }
  40.  
  41.    // Equivalent for runtime class associated with the
  42.    // managed wrapper generic argument type:
  43.  
  44.    public static bool UseExactMatch<T>() where T: DBObject
  45.    {
  46.      return ! typeof( T ).IsAbstract && UseExactMatch( RXClass.GetClass( typeof( T ) ) );
  47.    }
  48.  
  49.    public static bool UseExactMatch( Type type )
  50.    {
  51.      if( ! typeof( DBObject ).IsAssignableFrom( type ) )
  52.        throw new ArgumentException("Requires a type derived from DBObject");
  53.  
  54.      return ! type.IsAbstract && UseExactMatch( RXClass.GetClass( type ) );
  55.    }
  56.  
  57.    // Handle this to be notified when the runtime
  58.    // class tree has been rebuilt:
  59.  
  60.    public static event EventHandler Invalidated = null;
  61.  
  62.    static RuntimeClassInfo()
  63.    {
  64.      SystemObjects.DynamicLinker.ModuleLoaded += moduleLoadUnload;
  65.      SystemObjects.DynamicLinker.ModuleUnloaded += moduleLoadUnload;
  66.    }
  67.  
  68.    static void moduleLoadUnload( object sender, DynamicLinkerEventArgs e )
  69.    {
  70.      this.parents = null;
  71.      this.reset = true;
  72.      if( this.Invalidated != null )
  73.      {
  74.        this.Invalidated( null, EventArgs.Empty );
  75.      }
  76.    }
  77.  
  78.    internal static HashSet<IntPtr> Parents
  79.    {
  80.      get
  81.      {
  82.        if( this.parents == null )
  83.        {
  84.          this.parents = this.GetAllParents();
  85.        }
  86.        return this.parents;
  87.      }
  88.    }
  89.  
  90.    // This code can survive module load/unload events
  91.    // that fire while iterating over the ClassDictionary.
  92.    //
  93.    // If that happens, the code will abort the iteration,
  94.    // discard the result, and start iterating over the
  95.    // ClassDictionary again, and will do that repeatedly
  96.    // until it has completed without being interrupted by
  97.    // a module load/unload event. This is a necessary evil,
  98.    // because a module load/unload event can result in the
  99.    // the runtime class tree being rebuilt, invalidating
  100.    // the result.
  101.  
  102.    static HashSet<IntPtr> GetAllParents()
  103.    {
  104.      HashSet<IntPtr> items = new HashSet<IntPtr>();
  105.      RXClass baseClass = RXClass.GetClass( typeof( DBObject ) ).MyParent;
  106.      this.reset = true;
  107.      while( this.reset )
  108.      {
  109.        this.reset = false;
  110.        items.Clear();
  111.        foreach( DictionaryEntry e in SystemObjects.ClassDictionary )
  112.        {
  113.          RXClass rxclass = (RXClass) e.Value;
  114.          if( rxclass.IsDerivedFrom( baseClass ) )
  115.          {
  116.            RXClass parent = rxclass.MyParent;
  117.            if( parent != null )
  118.            {
  119.              items.Add( parent.UnmanagedObject );
  120.            }
  121.            if( this.reset )  
  122.            {                
  123.              break;    
  124.            }
  125.          }
  126.        }
  127.      }
  128.      items.TrimExcess();
  129.      return items;
  130.    }
  131.  
  132.  }
  133.  
  134. }
  135.  
It code not compiled, because "this." keyword can't be use in the static properties and methods.
« Last Edit: July 06, 2012, 08:28:21 am by Andrey »
Man is born for happyness like a bird for flying.

Andrey Bushman

  • Swamp Rat
  • Posts: 592
  • CAD admin, programmer.
Re: Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« Reply #8 on: July 06, 2012, 09:12:09 am »
I knew there was a major optimization that I was just not
seeing, and I think I've nailed it.

In the previously posted code, GetObjects<T>() must call
RXClass.IsDerivedFrom() on the ObjectClass property of
every single ObjectId in the sequence.

Well, doing that is pointless if there are no derived types
(for example, objects like BlockReference often have no
types derived from them, unless there are custom objects
present that derive from those types).

In this major revision, the code will monitor changes to the
ObjectARX runtime class tree, and will keep track of whether
each RXClass that's used with IsTypeOf<T>() (and its cousins)
have any child RXClasses.

In cases where the RXClass associated with the managed wrapper
type used as the generic argument has no derived RXClasses, the
code avoids calling IsDerivedFrom() on the ObjectClass of every
ObjectId in the sequence, resulting in a major performance gain.

Code - C#: [Select]
  1.  
  2.  public static class AcRxExtensionMethods
  3.  {
  4.    // ObjectId.IsTypeOf<T>() extension method:
  5.    //
  6.    // returns true if the managed wrapper returned
  7.    // by opening the given ObjectId is an instance
  8.    // of the generic argument type or a derived type.
  9.    //
  10.    // Example: Tells if the DBObject referenced by
  11.    // the given ObjectId is an instance of a Curve:
  12.    //
  13.    //   ObjectId id = GetSomeObjectId();
  14.    //
  15.    //   bool isCurve = id.IsTypeOf<Curve>();
  16.    //
  17.    // The advantage in the above use of IsTypeOf<T>() is
  18.    // that it doesn't require the RXClass for Curve to be
  19.    // fetched on each call, because it is already cached
  20.    // in a static member of the generic RXClass<Curve> type.
  21.    //
  22.    // The other benefit is that IsTypeOf<T>() can be
  23.    // used anywhere, and without a transaction, since
  24.    // it doesn't need to open the database object.
  25.    //
  26.    // The same can also be done more directly,
  27.    // but less-intuitively:
  28.    //
  29.    //   bool isCurve = id.ObjectClass.IsAssignableTo<Curve>();
  30.    //
  31.    //
  32.    // And of course, without using any of these helper APIs,
  33.    // one would need to do:
  34.    //
  35.    //   bool isCurve = id.ObjectClass.IsDerivedFrom(
  36.    //                       RXClass.GetClass( typeof( Curve ) )
  37.    //                  );
  38.    //
  39.  
  40.    public static bool IsTypeOf<T>( this ObjectId id )
  41.      where T: DBObject
  42.    {
  43.      return RXClass<T>.IsAssignableFrom( id.ObjectClass );
  44.    }
  45.  
  46.    // Like IsTypeOf<T>, except ids whose opened managed wrapper
  47.    // type are derived from T, but are not T itself, do not match.
  48.    //
  49.    // Use this method when you require all ids to be instances
  50.    // of concrete types (e.g., BlockReference, DBText, etc.), and
  51.    // there is no chance that any objects will be derived from
  52.    // the given type. Using abstract types as the generic argument
  53.    // (e.g., Curve, Entity, DBObject, etc.) is not valid, as the
  54.    // result will always be false:
  55.    //
  56.    // This method is faster than IsTypeOf<T>.
  57.  
  58.    public static bool IsInstanceOf<T>( this ObjectId id )
  59.      where T: DBObject
  60.    {
  61.      return RXClass<T>.unmanagedObject == id.ObjectClass.UnmanagedObject;
  62.    }
  63.  
  64.    // returns true, if a managed wrapper with the given
  65.    // RXClass can be cast to a variable of type T, where
  66.    // T is DBObject or any derived type:
  67.  
  68.    public static bool IsAssignableTo<T>( this RXClass rxclass )
  69.      where T: DBObject
  70.    {
  71.      return RXClass<T>.IsAssignableFrom( rxclass );
  72.    }
  73.  
  74.    // IEnumerable<ObjectId>.OfType<T>() Extension:
  75.    //
  76.    // The OfType<T> extension method is the plural form
  77.    // of IsTypeOf<T>, that reduces an IEnumerable<ObjectId>
  78.    // sequence to only those ids whose managed wrapper
  79.    // type is an instance of the generic argument type.
  80.    //
  81.    // Here we reduce an ObjectIdCollection to a sequence
  82.    // containing only the ObjectIds of Circle entities:
  83.    //
  84.    //   ObjectIdCollection entityIds = GetSomeObjectIdCollection();
  85.    //
  86.    //   var circleIds = entityIds.OfType<Circle>();
  87.  
  88.    // Can be invoked on ObjectId[] arrays or List<ObjectId>:
  89.  
  90.    public static IEnumerable<ObjectId> OfType<T>( this IEnumerable<ObjectId> source )
  91.      where T: DBObject
  92.    {
  93.      ObjectId[] array = source as ObjectId[];
  94.      if( array != null )
  95.        return OfTypeIterator( array );
  96.      else
  97.        return src.Where( IsTypeOf<T> );
  98.    }
  99.  
  100.    // Overload for ObjectIdCollections:
  101.  
  102.    public static IEnumerable<ObjectId> OfType<T>( this ObjectIdCollection ids )
  103.      where T: DBObject
  104.    {
  105.      return OfTypeIterator<T>( ids );
  106.    }
  107.  
  108.    // Overload for BlockTableRecord:
  109.  
  110.    public static IEnumerable<ObjectId> OfType<T>( this BlockTableRecord btr, bool includingErased )
  111.      where T: DBObject
  112.    {
  113.      return OfTypeIterator<T>( includingErased ? btr.IncludingErased : btr );
  114.    }
  115.  
  116.    static IEnumerable<ObjectId> OfTypeIterator<T>( this IEnumerable ids )
  117.      where T: DBObject
  118.    {
  119.      foreach( ObjectId id in ids )
  120.      {
  121.        if( RXClass<T>.IsAssignableFrom( id.ObjectClass )
  122.          yield return id;
  123.      }
  124.    }
  125.  
  126.    // Specialize the case for array and ObjectIdCollection
  127.    // sources, both of which can be iterated faster using
  128.    // their indexers, verses their IEnumerator:
  129.  
  130.    static IEnumerable<ObjectId> OfTypeIterator<T>( this ObjectId[] ids )
  131.      where T: DBObject
  132.    {
  133.      int cnt = ids.Length;
  134.      for( int i = 0; i < cnt; i++ )
  135.      {
  136.        ObjectId id = ids[i];
  137.        if( RXClass<T>.IsAssignableFrom( id.ObjectClass ) )
  138.          yield return id;
  139.      }
  140.    }
  141.  
  142.    static IEnumerable<ObjectId> OfTypeIterator<T>( this ObjectIdCollection ids )
  143.      where T: DBObject
  144.    {
  145.      int cnt = ids.Count;
  146.      for( int i = 0; i < cnt; i++ )
  147.      {
  148.        ObjectId id = ids[i];
  149.        if( RXClass<T>.IsAssignableFrom( id.ObjectClass ) )
  150.          yield return id;
  151.      }
  152.    }
  153.  
  154.    ////////////////////////////////////////////////////////////////////////////
  155.    // IEnumerable<ObjectId>.AreAnyOfType<T>():
  156.    //
  157.    // Return true if at least one ObjectId in the source references a
  158.    // database object whose opened managed wrapper type is an instance of T:
  159.  
  160.    public static bool AreAnyOfType<T>( this IEnumerable<ObjectId> ids )
  161.      where T: DBObject
  162.    {
  163.      return ids.Any( IsTypeOf<T> );
  164.    }
  165.  
  166.    public static bool AreAnyOfType<T>( this ObjectIdCollection ids )
  167.      where T: DBObject
  168.    {
  169.      int cnt = ids.Count;
  170.      for( int i = 0; i < cnt; i++ )
  171.      {
  172.        if( ids[i].IsTypeOf<T>() )
  173.          return true;
  174.      }
  175.      return false;
  176.    }
  177.  
  178.    ////////////////////////////////////////////////////////////////////////////
  179.    // IEnumerable<ObjectId>.AreAllOfType<T>():
  180.    //
  181.    // Return true if all ObjectIds in the source reference database
  182.    // objects whose opened managed wrapper type is an instance of T.
  183.    //
  184.    // This method is useful for validating the contents of
  185.    // an ObjectIdCollection or array of Object[], passed as
  186.    // an argument to an API.
  187.    //
  188.  
  189.    public static bool AreAllOfType<T>( this IEnumerable<ObjectId> ids
  190.      where T: DBObject;
  191.    {
  192.      return ids.All( IsTypeOf<T> );
  193.    }
  194.  
  195.    public static bool AreAllOfType<T>( this ObjectIdCollection ids )
  196.      where T: DBObject;
  197.    {
  198.      int cnt = ids.Count;
  199.      for( int i = 0; i < cnt; i++ )
  200.      {
  201.        if( ! ids[i].IsTypeOf<T>() )
  202.          return false;
  203.      }
  204.      return true;
  205.    }  
  206.  
  207.    // Returns true if the given RXClass is the RXClass of the
  208.    // managed wrapper of type T:
  209.  
  210.    public static bool EqualsObjectClass<T>( this RXClass rxclass )
  211.      where T: DBObject;
  212.    {
  213.      return rxclass.UnmanagedObject == RXClass<T>.unmanagedObject;
  214.    }
  215.  
  216.    // RXClass<T> Class:
  217.    //
  218.    // RXClass<T> caches the RXClass and value of its UnmanagedObject
  219.    // property for each managed wrapper type T, allowing extension
  220.    // methods of this class to avoid the boxing operation incurrred
  221.    // by using the DisposableWrapper's == operator. A bool indicating
  222.    // if the RXClass has children (derived RXClasses) is cached and
  223.    // updated whenever the ObjectARX runtime class tree is rebuilt.
  224.    //
  225.    // This class handles the ModuleLoaded/unload events, so that it
  226.    // can update its hasChildren member whenever the ObjectARX runtime
  227.    // class hierarchy is rebuilt, which could result in the addition
  228.    // or deletion of child RXClasses. Tracking whether this RXClass has
  229.    // derived/child RXClasses allows us to avoid a pointless call to
  230.    // RXClass.IsDerivedFrom() on RXClasses we compare to this RXClass.
  231.    //
  232.    // Caching the UnmanagedObject property value of each RXClass
  233.    // also exploits a limitation that precludes methods of types
  234.    // deriving from MarshalByRefObject from being inlined.
  235.  
  236.    static class RXClass<T> where T: DBObject
  237.    {
  238.      public static RXClass instance;
  239.      public static IntPtr unmanagedObject;
  240.      public static bool hasChildren;
  241.  
  242.      static RXClass()
  243.      {
  244.        Initialize();
  245.        SystemObjects.DynamicLinker.ModuleLoaded += moduleLoadUnload;
  246.        SystemObjects.DynamicLinker.ModuleUnloaded += moduleLoadUnload;
  247.      }
  248.  
  249.      public static bool IsAssignableFrom( RXClass rxclass )
  250.      {
  251.        return rxclass.UnmanagedObject == unmanagedObject
  252.          || ( hasChildren && rxclass.IsDerivedFrom( instance ) );
  253.      }
  254.  
  255.      public static bool Equals( RXClass rxclass )
  256.      {
  257.        return rxclass != null && rxclass.UnmanagedObject == unmanagedObject;
  258.      }
  259.  
  260.      static void moduleLoadUnload( object sender, DynamicLinkerEventArgs e )
  261.      {
  262.        Initialize();
  263.      }
  264.  
  265.      // The cost of this is high, and will happen every time
  266.      // a module is loaded and unloaded, but the performance
  267.      // gain resulting from caching this data is significant
  268.      // and far outweighs the slight delay imposed whenever
  269.      // a module is loaded or unloaded. It should be pointed
  270.      // out that the above event handlers are not added until
  271.      // the first use of code that uses IsTypeOf<T>, for each
  272.      // distinct type used as the generic argument.
  273.  
  274.      static void Initialize()
  275.      {
  276.        instance = RXClass.GetClass( typeof( T ) );
  277.        unmanagedObject = instance.UnmanagedObject;
  278.        hasChildren = HasChildren();
  279.      }
  280.  
  281.  
  282.      // Returns a bool indicating if at least one RXClass is
  283.      // directly derived from the RXClass instance member.
  284.      //
  285.      // If the result is false, this RXCLass has no children
  286.      // (e.g., there are no RXClasses derived from this RXClass),
  287.      // and hence, there is no point to calling IsDerivedFrom()
  288.      // on any other RXClass to find out if it is derived from
  289.      // this RXClass.
  290.      //
  291.      // In most cases where GetObjects<T> is used, the generic
  292.      // argument type will be a concrete type of entity, like
  293.      // BlockReference, Polyline, etc. Unless there are custom
  294.      // objects that derive from those concrete types (which is
  295.      // unlikely), there will be no runtime classes that derive
  296.      // from the RXClass for the managed wrapper type used as
  297.      // the generic argument, and we can avoid the cost of a
  298.      // call to RXClass.IsDerivedFrom() on the ObjectClass of
  299.      // every ObjectId in the sequence.
  300.  
  301.      static bool HasChildren()
  302.      {
  303.        foreach( DictionaryEntry e in SystemObjects.ClassDictionary )
  304.        {
  305.          RXClass rxclass = e.Value as RXClass;
  306.          if( rxclass != null && Equals( rxclass.MyParent ) )
  307.            return true;
  308.        }
  309.        return false;
  310.      }  
  311.  }
  312.  
It code too not compile, because in it there are many errors.
for example:
code row 90 has "source", but code row 97 use "src".
Code row 189 no has closed bracket.
Code row 190 has symbol ";" but this is violation of C# syntax.
The same mistakes in code rows 196, 211.
No closed bracket for namespace.
and e.t.c.
I tryed to fix all errors, but Visual Studio 2010 do not want to compile "OfTypeIterator(array);" and "this.".
« Last Edit: July 06, 2012, 09:24:20 am by Andrey »
Man is born for happyness like a bird for flying.

TT

  • Swamp Rat
  • Posts: 826
Re: Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« Reply #9 on: July 06, 2012, 10:10:56 am »
Hi Andrey, thanks for pointing out those errors. Yes the code was never compiled, it was quickly-written prototype code written as proof of concept. I was planning on doing a real implementation of it and integrating it into my working when I got the time.

About the OfTypeIterator<T> methods, you can remove the 'this', and it should compile.

If I can find the time, I'll build and fix the errors and types and post a working version of the code.

« Last Edit: July 06, 2012, 10:27:43 am by TheMaster »

Andrey Bushman

  • Swamp Rat
  • Posts: 592
  • CAD admin, programmer.
Re: Quick & Easy Managed Wrapper Type Discovery from ObjectIds
« Reply #10 on: July 06, 2012, 11:03:12 am »
Hi Andrey, thanks for pointing out those errors. Yes the code was never compiled, it was quickly-written prototype code written as proof of concept. I was planning on doing a real implementation of it and integrating it into my working when I got the time.

About the OfTypeIterator<T> methods, you can remove the 'this', and it should compile.

If I can find the time, I'll build and fix the errors and types and post a working version of the code.
I will grateful to you. This topic is very interesting for me.

Regards
Man is born for happyness like a bird for flying.