Har en klass som innehåller en SafeHandle ärvd typ. Sätt objektet till null, sen GC.Collect() Du bör implementer IDisposable interfacet istället för att bara implementera en finalizer. Det finns dessutom ett pattern från MS för hur det ska gå till. Lite mer förklaring.Testa finalizer
Finns det någon möjlighet att testa att ReleaseHandle gör det den ska i finalizern?
(Det är en ganska enkel funktion så det finns egentligen ingen anledning att testa den men jag blev nyfiken på om det är möjligt)Sv: Testa finalizer
obs endast för testning, ej bra prestanda att tvinga GC att städa för ofta.Sv: Testa finalizer
Implementera IDisposable's Dispose genom att anropa en protected viod Disposable(bool disposing) som du skapat. Anropa den med true. I den metoden rensar du upp allt du vill i dispose om disposing=true. Från Finalizern anropar du metoden med false. På detta vis vet du i Disposing metoden om det är en finalizer eller dispose som anropats och kan handla ut ifrån detta, dvs inte förutsätta att du kan anrop metoder på andra objekt om det är finalizern osv. Sen ska du dessutom hålla koll på om Dispose anropats och kasta en ObjectDisposedException ifrån alla metoder och properties som anropas efter. Typ:
public class MyClass : IDisposable
{
bool _disposed = false;
public void MyMethod()
{
if (_disposed)
throw new ObjectDisposedException();
// Do something
}
public void Dispose()
{
Dispose(true);
}
public void Dispose(bool disposing)
{
if (disposing)
{
// Städa upp. Alla objekt finns kvar och kan anropas.
// Anropa dispose på andra objekt osv...
// Hindra GC:n att finaliza
GC.SupressFinalize();
_disposed = true;
}
// Sätt alla objekt till null och gör det som kan göras från finalizern...
// Objekt kan vara finalizade
}
~MyClass()
{
Dispose(false);
}
}
På så vis kan du debugga din finalizer genom att bara anropa Dispose och dessutom är det "rätt" gjort.
Alternativet är att skapa ditt objekt. Sätta referencen till null och sedan anropa Garbage Collectorn (GC.Collect()). På detta vis kan du debugga din finalizer. Men du bör implementera IDisposable. Annars kommer du försena frigörande av minne med mera. Behövs en finalizer ska det i princip alltid implementeras IDisposable för att låta användaren frigöra minnet...
Sv:Testa finalizer
Jag har en klass som är en wrapper för en dll som kräver att man loggar in och loggar ut.
Därför har jag skapat en CriticalHandle (som är en SafeHandle utan refcount) som ser ut så här:
private class MySafeHandle : CriticalHandle {
public MySafeHandle(IntPtr p) : CriticalHandle(IntPtr.Zero) {
SetHandle(p);
}
public virtual bool IsInvalid { get { return handle == IntPtr.Zero; } }
protected virtual bool ReleaseHandle() override {
WrappedObject.LogOut(handle); return true;
}
}
Min klass ser sedan ut så här:
public class Wrapper : IDisposable {
private MySafeHandle myhandle;
public Wrapper() {
myhandle = new MySafeHandle(WrappedObject.Login());
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (myHandle != null && !myHandle.IsInvalid)
myHandle.Dispose();
}
}
Som jag sa så är ReleaseHandle så enkel i det här fallet att den inte behöver testas men tänk om den vore mer avancerad. Kanske det behövs en invoke för att göra logout i rätt tråd.
Som Chris sa så skulle jag ju via reflection kunna anropa Dispose(false). Dispose(false) leder dock till Dispose(true) för safehandler så blir det verkligen samma sak? En skillnad är ju också att anropet inte sker från GC tråden.