Antagligen är det jag som är dum men jag förstår inte hur jag skall implementera dispose korrekt. 1. Ja du bör implementera IDisposable (FxCop/"Code Analysis i Team versionerna" skulle för övrigt ha berättat detta för dej) Tack för ett snabbt och bra svar. Se även senaste nummret av MSDN Magazine:Dispose
Antag att jag har en klass som hanterar en fil (t.ex. en loggfil)
i C# ser det ut ungefär så här:
public class Logger {
private StreamWriter mLogFile;
public Logger() {
mLogFile = new StreamWriter(filnamn);
}
public void DoLog(string message) {
mLogFile.WriteLine(message);
}
};
Fråga 1: Nu är det ju så att StreamWriter implementerar IDisposable. Betyder inte det att min klass också bör implementera IDisposable för att frigöra filen när man är klar? (för en loggfil spelar det antagligen ingen roll men strunt i det).
Fråga 2: Vilken sorts dispose skall jag implementera? Räcker det med Dispose() eller skall jag också implementera Dispose(bool).
Fråga 3: Vilka tester skall jag ha i Dipose funktionen? Har sett många varianter som
if (mLogFile != null)
mLogFile.Dispose()
och
if (!disposed) {
if (mLogFile != null) mLogFile.Dispose()
disposed = true;
}
och varianter där mLogFile sätts till null efter Dispose.
När jag söker på nätet hittar jag inga entydiga svar och de blandar alltid in finalizers. Har själv aldrig förstått poängen med finalizers. Om man har resurser som bör frigöras och så bör man väl göra det så tididigt som möjligt med Dispose?
Som jag tolkat det tidigare ser en korrekt dispose ut så här:
private bool disposed = false;
public void Dispose() {
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
if (!disposed && disposing) {
mLogFile.Dispose();
disposed = true;
}
}
Men jag ser sällan den varianten i verklig kod.
Sv: Dispose
2. Ifall din klass är sealed så räcker Dispose().
virtual Dispose(bool) måste finnas med för att det ska kunna funka korrekt när arv är inblandat.
3. ditt "korrekta exempel" ser rätt bra ut, ifall det finns risk att mLogFile är null bör du naturligtvis testa detta först.
De exempel där det används
if (foo != null) {
foo.Dispose();
foo = null;
}
har oftast inte någon disposed flagga, nullchecken används som "substitut" för detta.
Att det ofta slarvas i riktig kod är tråkigt, men tyvärr sant.. :(
Sv:Dispose
>1. Ja du bör implementera IDisposable (FxCop/"Code Analysis i Team versionerna" skulle för övrigt ha berättat detta för dej)
Förstår inte varför man inte får en varning i kompilatorn för varesig saknad IDisposable eller anrop till Dispose. FxCop är ju inget man kör så ofta.
Det som gör mig lite förvillad är att många klasser verkar ha både Close och Dispose. (t.ex. XMLReader)
- Behöver man då anropa Dispose om man anropat Close
- Skall man anropa Close i Dispose(bool)
- Är det bättre att göra
try
foo = XmlReader.Create...
finally
foo.Close();
än
using (foo = XmlReader.Create..) {
}
>2. Ifall din klass är sealed så räcker Dispose().
>virtual Dispose(bool) måste finnas med för att det ska kunna funka korrekt när arv är inblandat.
Och Microsofts rekommendation är "Do not seal classes without having a good reason to do so" så Dispose(bool) bör i princip alltid användas.
>3. ditt "korrekta exempel" ser rätt bra ut, ifall det finns risk att mLogFile är null bör du naturligtvis testa detta först.
Ok. Som du förstår kommer jag från C++ och jag glömmer ibland att Dispose inte fungerar som en destruktor (I C++ anropas ju inte destruktorn om ett undantag inträffar i konstruktorn). Man kan ju aldrig veta om alla objekt är skapade så man måste alltid testa mot null i Dispose().
Skall uppdatera min dispose-snippet till att testa mot null istället för disposed variabeln.
#region IDisposable Members
public void Dispose() { Dispose(true); }
protected virtual void Dispose(bool disposing) {
if (disposing) {
if (object != null) object.Dispose();
object = null;
}
}
#endregion
Sv: Dispose
http://msdn.microsoft.com/msdnmag/issues/07/07/CLRInsideOut/default.aspx