-----------------------------------

Acquista i software ArcGIS tramite Studio A&T srl, rivenditore autorizzato dei prodotti Esri.

I migliori software GIS, il miglior supporto tecnico!

I migliori software GIS, il miglior supporto tecnico!
Azienda operante nel settore GIS dal 2001, specializzata nell’utilizzo della tecnologia ArcGIS e aderente ai programmi Esri Italia Business Network ed Esri Partner Network

-----------------------------------



domenica 13 giugno 2010

Singletons e System.__COMObject

Quando un ArcObject (basato su COM) in ambiente .NET viene istanziato via interop con la parola riservata 'new', viene preso un riferimento all'oggetto stesso tramite un wrapper .NET Framework fortemente tipizzato (runtime callable wrapper (RCW)). Un RCW è un oggetto gestito che può trattenere un oggetto COM in una applicazione .NET.

Vediamo un esempio:

IEnvelope env = new EnvelopeClass();


In questo caso, in fase di compilazione, il compilatore conosce il tipo esatto della variabile tramite Reflection e metadati ed a runtime utilizza questa informazione per sapere il tipo esatto della variabile.

Vediamo un altro esempio:

            IEnvelope a = axMapControl1.Extent;

            try
            {
                EnvelopeClass b = (EnvelopeClass)a;
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(ex.Message);
            }


In questo caso, quando il codice è compilato, il compilatore sa solo che la proprietà Extent restituisce un oggetto che implementa l'interfaccia IEnvelope ma non può sapere il tipo della classe dell'oggetto. Sebbene possiamo sempre 'castare' ad interfacce che implementano Envelope, il runtime .NET 'wrappa' il riferimento all'oggetto COM tramite un RCW generico chiamato System.__ComObject.
Riprendendo l'esempio, se proviamo a 'castare' la variabile a in EnvelopeClass otterremmo un errore perchè non possiamo 'castare' da un System.__ComObject ad una classe.



Possiamo, come abbiato detto, castare mediante le interfacce che sono implementate dall'oggetto ma, come potete vedere, l'oggetto è un System.__ComObject.

Invece nel primo esempio che abbiamo visto l'rcw è fortemente tipizzato:




Vediamo ora cosa succede con gli oggetti Singleton.
Gli oggetti singleton supportano una sola istanza dell'oggetto (vedi post).
Dalla ArcGIS 9.x, gli oggetti singleton sono singletons per thread e non per processo (vedi post).

Anche qui vale quello che abbiamo detto precedentemente solo che, se dovessimo istanziare un nuovo oggetto, prenderemmo come riferimento l'istanza corrente.
Per esempio:
IWorkspaceFactory workspaceFactory = new AccessWorkspaceFactoryClass();

            IWorkspaceFactory workspaceFactory1 = new AccessWorkspaceFactoryClass();

            if (object.ReferenceEquals(workspaceFactory, workspaceFactory1))
                System.Windows.Forms.MessageBox.Show("workspaceFactory and workspaceFactory1: same reference");


            SpatialReferenceEnvironment spatialReferenceEnvironment = new SpatialReferenceEnvironmentClass();
            SpatialReferenceEnvironment spatialReferenceEnvironment1 = new SpatialReferenceEnvironmentClass();

            if (object.ReferenceEquals(spatialReferenceEnvironment, spatialReferenceEnvironment1))
                System.Windows.Forms.MessageBox.Show("spatialReferenceEnvironment and spatialReferenceEnvironment1: same reference");

AccessWorkspaceFactoryClass e SpatialReferenceEnvironment sono singleton (vedi qui l'elenco degli oggetti singleton negli arcobjects).  In questo caso, dopo aver istanziato la classe, se la dovessimo reinstanziare, otterremmo lo stesso riferimento (verifica con il metodo ReferenceEquals). Non si può dire lo stesso di una classe non singleton. Env e env1 non rappresentano la stessa istanza:

IEnvelope env = new EnvelopeClass();
            IEnvelope env1 = new EnvelopeClass();
            if (object.ReferenceEquals(env, env1))
                System.Windows.Forms.MessageBox.Show("env and env1: same reference");
            else
                System.Windows.Forms.MessageBox.Show("env and env1: Not the same reference");

Con gli oggetti singleton, a differenza di quelli non singleton, potremmo incorrere nell'errore precentemente visto ("Impossibile eseguire il cast...") anche con un semplice new

IWorkspaceFactory workspaceFactory = new AccessWorkspaceFactoryClass();


Questo perchè è possibile che sia presente già la singola istanza come RCW non fortemente tipizzato e quindi il cast fallisce anche se si fa riferimento con un'interfaccia perchè il new tenta di 'castare' da RCW non tipizzato a RCW tipizzato e l'operazione fallisce. Ad esempio tools in ArcObjects scritti .NET da terze parti potrebbero 'wrappare' un oggetto in un generico wrapper causando l'errore.

In questo esempio non fallisce perchè supponiamo di trovarci nel caso in cui non siamo in presenza di una istanza già presente (AccessWorkspaceFactoryClass) e non tipizzata ( System.__ComObject ) e quindi il cast viene eseguito:

IWorkspaceFactory workspaceFactory = new AccessWorkspaceFactoryClass();
IWorkspaceFactory workspaceFactory1 = new AccessWorkspaceFactoryClass();


Per evitare comunque il possibile errore precedentemente descritto è buona regola utilizzare la classe Activator per assicurare che l'oggetto singleton sia wrappato con un rcw fortemente tipizzato quando fai riferimento ad esso per la prima volta. Usando il metodo CreateInstance della class Activator invece di usare il new, l'Activator è abilitato a prendere le informazioni (metadato) per eseguire il cast.

Type factoryType = Type.GetTypeFromProgID(
                  "esriDataSourcesGDB.AccessWorkspaceFactory");
            IWorkspaceFactory workspaceFactory = (IWorkspaceFactory)
              Activator.CreateInstance(factoryType);

Nessun commento: