So werden Sie benachrichtigt, bevor statische Variablen finalisiert werden
Wann kann ich Objekte bereinigen, die in statischen Variablen in C # gespeichert sind?
Ich habe also eine statische Variableträge initialisiert:
public class Sqm
{
private static Lazy<Sqm> _default = new Lazy<Sqm>();
public static Sqm Default { get { return _default.Value; } }
}
Hinweis: Das habe ich gerade geändertFoo
ein ... zu seinstatic
Klasse. Es ändert nichts an der Frage, obFoo
ist statisch oder nicht. Aber einige Leute sind davon überzeugt, dass es keinen Weg gibt, einen Fall vonSqm
könnte konstruiert werden, ohne zuerst eine Instanz vonFoo
. Selbst wenn ichtat ein ... kreierenFoo
Objekt; Selbst wenn ich 100 davon erschaffen hätte, würde es mir nicht helfen, das Problem zu lösen (von wann bis wann)"Aufräumen" ein statisches Mitglied).
Beispielnutzung
Foo.Default.TimerStart("SaveQuestion");
//...snip...
Foo.Default.TimerStop("SaveQuestion");
Jetzt meinSqm
class implementiert eine Methode, die aufgerufen werden muss, wenn das Objekt nicht mehr benötigt wird und sich selbst bereinigen muss (Zustand im Dateisystem speichern, Sperren aufheben usw.). Diese MethodeMuss aufgerufen werden, bevor die Garbage Collectors ausgeführt werden (d. h. bevor der Finalizer meines Objekts aufgerufen wird):
public class Sqm
{
var values = new List<String>();
Boolean shutdown = false;
protected void Cleanup(ICollection stuff)
{
WebRequest http = new HttpWebRequest();
http.Open("POST", "https://stackoverflow.com/SubmitUsageTelemetry");
http.PostBody = stuff;
http.Send();
}
public void Shutdown()
{
if (!alreadyShutdown)
{
Cleanup(values);
alreadyShutdown = true;
}
}
}
Wann und wo kann ich meine anrufen?Shutdown()
Methode?
Hinweis: Ich will nicht den Entwickler, derVerwendet dasSqm
Klasse, um sich Sorgen über das Anrufen zu machenShutdown
. Das ist nicht sein Job. In anderen Sprachumgebungen würde er nicht müssen.
DasLazy<T>
Klasse scheint nicht zu nennenDispose
auf derValue
es besitzt faul. Also kann ich das nicht hakenIDisposable
Muster - und verwenden Sie das als die Zeit zum AnrufenShutdown
. Ich muss anrufenShutdown
mich selber.
Aber wenn?
Es ist einstatic
variabel, es existiert einmal für die Lebensdauer der Anwendung / Domain / Appdomain / Wohnung.
Manche Leute verstehen, und manche nicht, dass sie versuchen, meine Daten während einesfinalizer
istfalsch.
///WRONG: Don't do this!
~Sqm
{
Shutdown(_values); //<-- BAD! _values might already have been finalized by the GC!
}
Warum ist es falsch? weilvalues
könnte nicht mehr da sein. Sie steuern nicht, welche Objekte in welcher Reihenfolge finalisiert werden. Es ist durchaus möglich, dassvalues
wurde vor dem Enthalten abgeschlossenSqm
.
DasIDisposable
Schnittstelle und dieDispose()
Methode ist einKonvention. Es gibt nichts, was dies vorschreibt, wenn mein Objekt a implementiertDispose()
Methode, die es jemals aufgerufen wird. In der Tat, ichkönnte Gehen Sie voran und implementieren Sie es:
public class Sqm : IDisposable
{
var values = new List<String>();
Boolean alreadyDiposed = false;
protected void Cleanup(ICollection stuff)
{
WebRequest http = new HttpWebRequest();
http.Open("POST", "https://stackoverflow.com/SubmitUsageTelemetry");
http.PostBody = stuff;
http.Send();
}
public void Dispose()
{
if (!alreadyDiposed)
{
Cleanup(values);
alreadyDiposed = true;
}
}
}
Für die Person, die die Frage tatsächlich liest, könnte es sein, dass ich nichts geändert habe. Ich habe nur den Namen einer Methode von geändertAusschalten zuEntsorgen. Das Dispose-Muster ist einfach eine Konvention. Ich habe immer noch das Problem: Wann kann ich anrufen?Dispose
?
BerufungDispose
von meinem finalizer ist genauso falsch wie aufrufShutdown
von meinem Finalizer (sie sind identisch falsch):
public class Sqm : IDisposable
{
var values = new List<String>();
Boolean alreadyDiposed = false;
protected void Cleanup(ICollection stuff)
{
WebRequest http = new HttpWebRequest();
http.Open("POST", "https://stackoverflow.com/SubmitUsageTelemetry");
http.PostBody = stuff;
http.Send();
}
public void Dispose()
{
if (!alreadyDiposed)
{
Cleanup(_values); // <--BUG: _values might already have been finalized by the GC!
alreadyDiposed = true;
}
}
~Sqm
{
Dispose();
}
}
Weil wiedervalues
könnte nicht mehr da sein. Der Vollständigkeit halber können wir zum vollständigen, korrekten Originalcode zurückkehren:
public class Sqm : IDisposable
{
var values = new List<String>();
Boolean alreadyDiposed = false;
protected void Cleanup(ICollection stuff)
{
WebRequest http = new HttpWebRequest();
http.Open("POST", "https://stackoverflow.com/SubmitUsageTelemetry");
http.PostBody = stuff;
http.Send();
}
protected void Dispose(Boolean itIsSafeToAlsoAccessManagedResources)
{
if (!alreadyDiposed)
{
if (itIsSafeToAlsoAccessManagedResources)
Cleanup(values);
alreadyDiposed = true;
}
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
~Sqm
{
Dispose(false); //false ==> it is not safe to access values
}
}
Ich habe den Kreis geschlossen. Ich habe ein Objekt, das ich brauche"Aufräumen" bevor die Anwendungsdomäne heruntergefahren wird. Etwas in meinem Objekt muss benachrichtigt werden, wenn es aufgerufen werden kannCleanup
.
Nein.
Ich migriere vorhandene Konzepte aus einer anderen Sprache in C #. Wenn ein Entwickler zufällig die globale Singleton-Instanz verwendet:
Foo.Sqm.TimerStart();
dann ist dieSqm
Klasse ist faul initialisiert. In einer (systemeigenen) Anwendung wird der Verweis auf das Objekt gespeichert. Während des (systemeigenen) Herunterfahrens der Anwendung wird die Variable, die den Schnittstellenzeiger enthält, auf gesetztnull
und das Singleton-Objektdestructor
heißt, und es kann sich aufräumen.
Niemand sollte jemals etwas anrufen müssen. NichtCleanup
nichtShutdown
nichtDispose
. Das Herunterfahren sollte automatisch durch die Infrastruktur erfolgen.
Was ist das C # -Äquivalent vonIch sehe mich gehen, mich aufräumen?
Erschwerend kommt hinzu, dass es zu spät ist, wenn der Müllmann das Objekt einsammelt. Die internen Statusobjekte, die ich beibehalten möchte, sind wahrscheinlich bereits finalisiert.
Es wäre einfach, wenn von ASP.netWenn ich garantieren könnte, dass meine Klasse von ASP.net verwendet wird, könnte ich das fragenHostingEnvironment
So benachrichtigen Sie mich vor dem Herunterfahren der Domain, indem Sie mein Objekt bei ihr registrieren:
System.Web.Hosting.HostingEnvironment.RegisterObject(this);
Und implementieren Sie dieStop
Methode:
public class Sqm : IDisposable, IRegisteredObject
{
var values = new List<String>();
Boolean alreadyDiposed = false;
protected void Cleanup(ICollection stuff)
{
WebRequest http = new HttpWebRequest();
http.Open("POST", "https://stackoverflow.com/SubmitUsageTelemetry");
http.PostBody = stuff;
http.Send();
}
protected void Dispose(Boolean itIsSafeToAlsoAccessManagedResources)
{
if (!alreadyDiposed)
{
if (itIsSafeToAlsoAccessManagedResources)
Cleanup(values);
alreadyDiposed = true;
}
}
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
Sqm
{
//Register ourself with the ASP.net hosting environment,
//so we can be notified with the application is shutting down
HostingEnvironment.RegisterObject(this); //asp.net will call Stop() when it's time to cleanup
}
~Sqm
{
Dispose(false); //false ==> it is not safe to access values
}
// IRegisteredObject
protected void Stop(Boolean immediate)
{
if (immediate)
{
//i took too long to shut down; the rug is being pulled out from under me.
//i had my chance. Oh well.
return;
}
Cleanup(); //or Dispose(), both good
}
}
Nur dass meine Klasse nicht weiß, von wo ich angerufen werdeASP.netoder vonWinFormsoder vonWPFoder eine Konsolenanwendung oder Shell-Erweiterung.
Bearbeiten: Die Leute scheinen verwirrt zu sein vonwas zumIDisposable
Muster existiert für. Verweise auf entferntDispose
um die Verwirrung zu beseitigen.
Bearbeiten 2: Die Leute scheinen einen vollständigen, detaillierten Beispielcode zu verlangen, bevor sie die Frage beantworten. Persönlich denke ich, dass die Frage bereits zu viel Beispielcode enthält, da es nicht dazu dient, die Frage zu stellen.
Und jetzt, wo ich hinzugefügt habesooo viel Code, die Frage ist verloren gegangen. Menschen weigern sich, eine Frage zu beantworten, bis die Frage gerechtfertigt ist. Jetzt, wo es gerechtfertigt ist, wird es niemand mehr lesen.
Es ist wie eine DiagnoseEs ist wie dasSystem.Diagnostics.Trace
Klasse. Die Leute nennen es, wenn sie wollen:
Trace.WriteLine("Column sort: {0} ms", sortTimeInMs);
und muss nie wieder daran denken.
Und dann setzt Verzweiflung einIch war sogar so verzweifelt, dass ich überlegte, mein Objekt hinter einem COM zu versteckenIUnknown
Schnittstelle, die Referenz gezählt wird
public class Sqm : IUnknown
{
IUnknown _default = new Lazy<Sqm>();
}
Und dann könnte ich hoffentlichTrick die CLR in die Referenzzählung auf meiner Schnittstelle zu dekrementieren. Wenn mein Referenzzähler Null wird, weiß ich, dass alles heruntergefahren wird.
Der Nachteil ist, dass ich es nicht zum Laufen bringen kann.