26 Stimmen

Neugierige C# mit Anweisungserweiterung

Ich habe ildasm ausgeführt, um dies festzustellen:

    using(Simple simp = new Simple())
    {
        Console.WriteLine("here");
    }

erzeugt IL-Code, der dem hier entspricht:

    Simple simp = new Simple();
    try
    {
        Console.WriteLine("here");
    }
    finally
    {
        if(simp != null)
        {
            simp.Dispose();
        }
    }

und die Frage ist, warum zur Hölle prüft es null in der finally? Der finally-Block wird nur ausgeführt, wenn der try-Block ausgeführt wird, und der try-Block wird nur ausgeführt, wenn der Simple-Konstruktor erfolgreich ist (d.h. keine Exception auslöst), in diesem Fall wird simp nicht null sein. (Wenn zu befürchten ist, dass zwischen dem Simple-Konstruktor und dem Beginn des Try-Blocks einige Zwischenschritte liegen, dann wäre das wirklich ein Problem, denn dann könnte eine Ausnahme ausgelöst werden, die die Ausführung des Finally-Blocks überhaupt verhindert). Also, warum zum Teufel?

Abgesehen von der Frage, ob die using-Anweisung besser ist als try-finally, schreibe ich meine try-finally-Blöcke wie folgt:

    Simple simp = new Simple();
    try
    {
        Console.WriteLine("here");
    }
    finally
    {
        simp.Dispose();
        simp = null;        // sanity-check in case I touch simp again
                            // because I don't rely on all classes
                            // necessarily throwing
                            // ObjectDisposedException
    }

0 Stimmen

Eine Sache hat mich neugierig gemacht: Wie "teuer" ist diese zusätzliche Unbedenklichkeitsprüfung (simp = null) im Vergleich zu der vom Compiler generierten Unbedenklichkeitsprüfung, was die Leistung angeht? Letztendlich scheint der Unterschied zwischen den beiden eher philosophischer als praktischer Natur zu sein, aber vielleicht irre ich mich ja. Interessant ist die Diskussion so oder so.

0 Stimmen

@Fredrik - Sie fragen also, ob "set to null" schneller/langsamer ist als "compare to null"? Ich bin mir nicht sicher. Abgesehen davon ist ein Vorteil der using-Anweisung, dass man sich keine Sorgen machen muss, dass auf das Objekt außerhalb des using-Bereichs zugegriffen wird. (Es sei denn, Sie halten einen anderen Verweis auf es.)

24 Stimmen

"Warum zum Teufel wird in der Endabfrage Null angezeigt?" Es gibt keinen guten Grund. Das Überspringen der Null-Prüfung ist eine Optimierung, die wir hätten durchführen können. Haben wir aber nicht. Keine wirklich große Sache; Nullprüfungen sind kurz und billig.

22voto

n8wrl Punkte 19091

Nein, der finally-Block wird IMMER ausgeführt. Möglicherweise erhalten Sie das Objekt nicht von einem new, sondern von einer anderen Funktion, die Ihr Objekt zurückgibt - und diese könnte NULL zurückgeben. using() ist Ihr Freund!

dss539 war so freundlich, mir vorzuschlagen, seine Notiz aufzunehmen:

using(Simple simp = null) 

ist ein weiterer Grund dafür, dass die Erweiterung zuerst auf Null geprüft werden muss.

2 Stimmen

Nur um ein wenig zu erweitern, könnten Sie das Factory-Pattern verwenden und etwas wie mit (myFactory.CreateMyObject()) tun, was zu einem Null-Objekt führen könnte.

10 Stimmen

Schlägt der Konstruktor jedoch fehl (vor dem try), wird das finally nicht ausgeführt.

11 Stimmen

Es wird davon ausgegangen, dass 'new' eine Exception auslöst, wenn es fehlschlägt, also niemals eine Null zurückgibt. Wenn die Ausnahme ausgelöst wird, wird der try-Block gar nicht erreicht und der finally-Block wird nicht ausgeführt. Der finally-Block wird also nicht IMMER ausgeführt.

11voto

dss539 Punkte 6619

using(Simple simp = null) ist noch eine andere Grund, dass die Erweiterung zuerst auf Null prüfen muss.

0 Stimmen

Wie lässt sich das überhaupt kompilieren? Es ist ein garantierter Laufzeitfehler.

2 Stimmen

Es ist nur dann ein garantierter Laufzeitfehler, wenn Sie das simp-Objekt verwenden. Zugegeben, es gibt keinen Grund für die Verwendung, wenn Sie es nicht....

1 Stimmen

Und obwohl es unwahrscheinlich ist, dass Sie explizit = null kodieren würden, könnte es = SomeFactoryMethod() sein, die null zurückgeben könnte.

4voto

Dolphin Punkte 4567

MSDN bei der using-Anweisung.

Was ich seltsam finde, ist, dass sie sich nicht ausdehnen lässt:

Simple simp = new Simple();
Simple __compilergeneratedtmpname = simp;
try
{
    Console.WriteLine("here");
}
finally
{
    if(__compilergeneratedtmpname != null)
    {
        __compilergeneratedtmpname.Dispose();
    }
}

1 Stimmen

Sie möchten die Möglichkeit haben, die simp Variable innerhalb des using-Blocks?

0 Stimmen

Ich wusste nicht, dass der Compiler simp als effektiv readonly erzwingt.

3 Stimmen

Delphin: Das würde beim Multithreading Probleme verursachen. Wenn der Konstruktor unmittelbar von einem Try gefolgt wird, garantiert die CLR, dass kein Thread in der Mitte "unterbrechen" würde. Auf deine Weise gibt es keine Garantie.

2voto

Joshua Drake Punkte 2626

Es scheint, dass Ihr Kommentar:

"Wenn zu befürchten ist, dass zwischen dem Simple-Konstruktor und dem Beginn des Try-Blocks einige Zwischenschritte liegen, wäre das wirklich ein Problem, denn dann könnte eine Ausnahme ausgelöst werden, die verhindert, dass der Finally-Block überhaupt ausgeführt werden kann."

ist möglicherweise goldrichtig. Siehe:

Atomarität und asynchrone Fehlfunktionen von Ausnahmen

Ich möchte auch das Problem (die Probleme) mit WCF und mit beachten:

Vermeiden von Problemen mit der Using-Anweisung und WCF Service Proxies auf die Bezug genommen wird:

Vermeiden von Problemen mit der Using-Anweisung

0voto

Der Code muss auf diese Weise übersetzt werden, um eine mögliche NullReferenceException wenn das Objekt entsorgt wird. Gemäß C#-Sprachreferenz El Anweisung verwenden akzeptiert nicht nur die Deklaration einer lokalen Variable als erstes Nichtterminal resource_acquisition Symbol, sondern jedes Ausdruck . Betrachten Sie den folgenden Code:

DisposableType @object = null;
using(@object) {
    // whatever
}

Es liegt auf der Hand, dass, sofern keine Null-Bedingung @object?.Dispose() im finnaly blockieren, würde es zu einer Ausnahme kommen. Die Nullprüfung ist nur dann überflüssig, wenn die Ausdruck von einem nicht-nullbaren Werttyp ist (nicht-nullbare Struktur). Und in der Tat, nach der oben erwähnten Sprachreferenz fehlt sie in einem solchen Fall.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X