Vediamo un esempio:
IEnvelope env = new EnvelopeClass();
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");
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);