Domanda

Sono bloccato con l'applicazione .Net 1.1 (cioè non posso usare i generics generics dalla 2.0 per ora), e stavo cercando di ottimizzare alcune parti del codice. Dato che si occupa molto di wrapper richiamabili in fase di runtime, che devono essere rilasciati, ho finito per creare un metodo di utilità che scorre fino a quando non vengono rilasciati tutti i riferimenti. La firma del metodo è:

void ReleaseObject(object comObject)

Dopo aver rilasciato tutti i comObjects, chiamo GC.Collect e GC.WaitForPendingFinalizers (non chiedere - lo sanno tutti coloro che hanno a che fare con l'interoperabilità di Office).

E ... come al solito, premo un caso angolare - se non assegno il riferimento gestito corrispondente a null prima della chiamata GC.Collect, non viene pulito correttamente.

Quindi, il mio codice è simile a:

ReleaseObject(myComObject);
myComObject = null;
GC.Collect()
...

Come, ci sono un sacco di xxx = null, ho deciso di metterlo nel metodo util, ma poiché c'è una differenza tra il passaggio per riferimento e il passaggio di un parametro di riferimento, ovviamente ho dovuto cambiare il metodo in:

void ReleaseObject(out object comObject)
{
   //do release
   comObject = null;
}

e modifica il chiamante in:

MyComClass myComObject = xxxx;
ReleaseObject(out myComObject);

Questo non riesce con un messaggio: " Impossibile convertire da "out MyComClass" a "out object" "

Mentre posso pensare al motivo per cui può essere un problema (ovvero il cast inverso dall'oggetto a MyComClass non è implicito e non vi è alcuna garanzia su cosa farà il metodo), mi chiedevo se ci fosse una soluzione alternativa, oppure devo rimanere con le mie centinaia di assegnazioni di null.

Nota: ho un sacco di diversi tipi di oggetti COM, ecco perché ho bisogno di un " oggetto " parametro e non di tipo sicuro.

È stato utile?

Soluzione

Perché è meglio chiamare un metodo piuttosto che impostare la variabile su null? Sono entrambe chiamate a linea singola e quest'ultima è molto più semplice.

Sembra strano però che tu debba prima impostarli su null. Sono variabili statiche o variabili di istanza i cui valori devono essere rilasciati prima del loro oggetto contenitore? Se la variabile è solo una variabile locale che andrà comunque al di fuori dell'ambito, impostandola su null non dovrebbe fare alcuna differenza (nella versione).

Gli RCW non implementano IDisposable? In tal caso, chiamare Dispose (preferibilmente tramite un'istruzione using) sarebbe la soluzione migliore.

(Dopo discussioni nei commenti.)

Queste sono variabili locali, a cui non verrà fatto riferimento più avanti nel metodo. Ciò significa che il garbage collector si renderà conto che non devono essere trattati come "root". riferimenti - quindi impostarli su null non dovrebbe fare alcuna differenza.

Per rispondere direttamente alla domanda originale, tuttavia: no, non puoi passare una variabile per riferimento a meno che il parametro del metodo non sia esattamente dello stesso tipo, quindi sei sfortunato qui. (Con i generici sarebbe possibile, ma hai detto che sei limitato a .NET 1.1.)

Altri suggerimenti

Sunny, ref e out sono suggerimenti per il marshalling + contratto per il compilatore. Ref e out sono un riporto ai giorni COM: i suggerimenti di smistamento per gli oggetti quando vengono inviati via cavo / tra processi.

Il contratto out

void foo( out MyClass x)
  1. foo () imposterà x su qualcosa prima che ritorni.
  2. x non ha alcun valore quando si immette foo () e si ottiene un errore del compilatore se si tenta di utilizzare x prima di impostarlo. (uso del parametro non assegnato x)

Il contratto ref

void foo( ref MyClass x)
  1. ref consente di modificare il riferimento dei chiamanti.
  2. x deve essere assegnabile
    • non puoi lanciare qualcosa su una variabile intermedia foo (ref (oggetto) qualcosa)
    • x non può essere una proprietà

È probabile che la realtà degli ultimi due punti ti impedisca di fare ciò che stai cercando di fare, perché in effetti non hanno senso quando capisci quali sono realmente i riferimenti. Se vuoi saperlo, chiedi a Jon Skeet (ha scritto un libro).

Quando si fa il marshalling di ref, si dice che oltre al valore di ritorno, riportano anche i valori di ref. Quando si esegue il marshalling, si dice che non si preoccupa di inviare il valore out quando viene chiamato il metodo, ma ricordare di riportare il valore out oltre al valore restituito.


DISCLAIMER DISCLAIMER DISCLAIMER

Come altri sottolineano, sta succedendo qualcosa di sospetto. Sembra che il codice di forza bruta che stai mantenendo abbia alcuni bug sottili e soffre di codifica per coincidenza. La soluzione migliore è probabilmente quella di aggiungere un altro livello di riferimento indiretto. vale a dire un wrapper per la classe wrapper che garantisce la pulizia deterministica in cui è possibile scrivere il codice disordinato una volta e una sola volta invece di scrutarlo in tutta la base di codice.


Detto questo ..

Alternativa 1

Ref non farà il trucco a meno che tu non fornisca sovraccarichi per ogni tipo di oggetto (com) con cui lo chiamerai.

// need a remove method for each type. 
void Remove( ref Com1 x ) { ...; x = null; }
void Remove( ref Con2 x ) { ...; x = null; }
void Remove( ref Com3 x ) { ...; x = null; }

// a generics version using ref.
void RemoveComRef<ComT>(ref ComT t) where ComT : class
{
    System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
    t = null; 
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
Remove( ref c1 );
RemoveComRef(ref c2); // the generics version again.

Alternativa 2

Se non vuoi farlo, restituisci null dal metodo Remove () e torna al tipo di oggetto.

class Remover
{
    // .net 1.1 must cast if assigning
    public static object Remove(object x)
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(x);
        return null;
    }

    // uses generics.
    public static ComT RemoveCom<ComT>(ComT t) where ComT : class
    {
        System.Runtime.InteropServices.Marshal.ReleaseComObject(t);
        return null;
    }   
}

Com1 c1 = new Com1();
Com2 c2 = new Com2();
c1 = (Com1)Remover.Remove(c1); // no reliance on generics
c2 = Remover.RemoveCom(c2); // relies on generics

* Ho aggiunto versioni generiche per il confronto.

Il codice sopra ha l'effetto che quando guardi il codice diventi sospettoso quando vedi una chiamata a Rimuovere (x) senza l'assegnazione (facendo apparire sbagliato il codice sbagliato). Potresti persino passare a Grep attraverso la base di codice in cerca di chiamate da rimuovere dove l'assegnazione non ha luogo.


DISCLAIMER: tutto quanto sopra è basato sulla necessità di impostare manualmente il riferimento su null, che (normalmente) non è necessario.

A mio avviso, non sarai in grado di impostare tali oggetti su null in un altro metodo (BTW dovresti usare il parametro ref invece di out per renderlo funzionante, comunque colpiresti lo stesso problema con l'errore "Impossibile convertire ...".) Consiglierei di creare e array di oggetti e di iterare attraverso quell'array, chiamando il metodo ReleaseObject e impostando tali oggetti su null. Qualcosa del tipo:

List garbagedObjects = new List();
garbagedObjects.Add(myComObject1);
garbagedObjects.Add(myComObject2);
...
foreach(object garbagedObject in garbagedObjects)
{
  ReleaseObject(garbagedObject);
  garbagedObject = null;
}
garbagedObjects = null;
GC.Collect();
...

Dovresti chiamare Marshal.ReleaseComObject , che AFAIK era disponibile in 1.1.

Probabilmente intendi " ref " ;:

static void ReleaseObject(ref object comObject)
{
   if(comObject != null)
   {
     //do release
     comObject = null;
   }
}

[modifica i commenti precedenti], tuttavia, funzionerà solo con oggetti non tipizzati, quindi non molto da usare senza generici! Oh per C # 2.0 ...

Re "quot" ref " ;; se le variabili sono veramente variabili (significato: variabili di metodo), presto usciranno dal campo di applicazione e verranno raccolte. Il "riferimento" sarebbe utile solo per rilasciare campi. Ma ad essere onesti, sarebbe più semplice impostarli su null ...

Un tipico modello COM è:

SomeType obj = new SomeType();
try {
  obj.SomeMethod(); // etc
} finally {
  Marshal.ReleaseComObject(obj);
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top