Ist es ein guter Ansatz, return inside mit der Anweisung {} aufzurufen?

90

Ich möchte nur wissen, ob es sicher / gut ist, returninnerhalb eines usingBlocks anzurufen .

Zum Beispiel.

using(var scope = new TransactionScope())
{
  // my core logic
  return true; // if condition met else
  return false;
  scope.Complete();
}

Wir wissen, dass die zuletzt geschweifte Klammer abgesagt dispose()wird. Aber was wird im obigen Fall sein, da returndie Kontrolle aus dem vorgegebenen Bereich (AFAIK) springt ...

  1. Wird ich scope.Complete()angerufen?
  2. Und so für die dispose()Methode des Oszilloskops.
Paolo Moretti
quelle
1
Sobald der using{}Bereich beendet ist, werden die relevanten Objekte entsorgt, return"brechen" den Bereich - so werden die Objekte wie erwartet entsorgt
Shai
2
Beachten Sie, dass Ihr scope.Complete()Anruf niemals von dem von Ihnen bereitgestellten Beispiel getroffen wird, sodass Ihre Transaktion immer zurückgesetzt wird.
Andy
Unabhängig davon, ob using's dispose()aufgerufen wird, wird bei der Rückkehr die Funktion, die diesen usingBlock enthält, zurückgegeben und alles, was dazu gehört, wird verwaist. Selbst wenn scopees nicht "von der using" entsorgt worden wäre (wie andere erklärten), wird es trotzdem entsorgt, weil die Funktion beendet wurde. Wenn C # eine gotoAussage hätte - hast du schon gelacht? gut - dann, anstatt zurückzukehren, könnten Sie gotonach der schließenden Klammer zurückkehren, ohne zurückzukehren. Logischerweise scopewäre es immer noch möglich, aber Sie haben gerade gotoC # eingegeben, also wen interessiert die Logik in diesem Stadium?
Superbest
C # hat gehe .
Jake T.

Antworten:

142

Es ist absolut sicher, returninnerhalb Ihres usingBlocks aufzurufen , da ein using-Block nur ein try/finallyBlock ist.

In Ihrem obigen Beispiel wird nach der Rückgabe trueder Bereich entsorgt und der Wert zurückgegeben. return falseund scope.Complete()wird nicht angerufen. Disposewird jedoch trotzdem aufgerufen, da es sich im finally-Block befindet.

Ihr Code ist im Wesentlichen derselbe wie dieser (wenn dies das Verständnis erleichtert):

var scope = new TransactionScope())
try
{
  // my core logic
  return true; // if condition met else
  return false;
  scope.Complete();
}
finally
{
  if( scope != null) 
    ((IDisposable)scope).Dispose();
}

Bitte beachten Sie, dass Ihre Transaktion niemals festgeschrieben wird , da es keine Möglichkeit gibt scope.Complete(), die Transaktion festzuschreiben.

Øyvind Bråthen
quelle
13
Sie sollten klar machen , dass Dispose wird aufgerufen. Wenn der OP nicht weiß, was passiert using, weiß er wahrscheinlich nicht, was passiert finally.
Konrad Rudolph
Es ist in Ordnung, einen using-Block mit return zu belassen
thewhiteambit
Nach meiner Erfahrung funktioniert dies nicht mit SQL Server CLR-Assemblys. Wenn ich Ergebnisse für eine UDF zurückgeben muss, die ein SqlXml-Feld enthält, das auf ein MemoryStream-Objekt verweist. Ich erhalte die Meldung " Kann nicht auf ein entsorgtes Objekt zugreifen " und " Ungültiger Versuch, Read aufzurufen, wenn der Stream geschlossen ist ". Daher bin ich gezwungen, in diesem Szenario undichten Code zu schreiben und auf die using-Anweisung zu verzichten. :( Meine einzige Hoffnung ist, dass die SQL CLR die Entsorgung dieser Objekte übernimmt. Es ist ein einzigartiges Szenario, aber ich dachte, ich würde es teilen.
MikeTeeVee
1
@MikeTeeVee - Reiniger - Lösungen sind entweder (a) die Anrufer haben die using, zum Beispiel using (var callersVar = MyFunc(..)) .., anstelle der Verwendung innerhalb des Habens „MyFunc“ - ich den Anrufer bedeutet den Strom gegeben wird , und ist verantwortlich für die es über Schließung usingoder explizit, oder (b) Lassen Sie MyFunc alle benötigten Informationen in andere Objekte extrahieren , die sicher zurückgegeben werden können. Dann können die zugrunde liegenden Datenobjekte oder Streams von Ihnen entsorgt werden using. Sie sollten keinen undichten Code schreiben müssen.
ToolmakerSteve
6

Das ist in Ordnung - finallyKlauseln (das ist, was die schließende geschweifte Klammer derusing Klausel unter der Haube tut) werden immer ausgeführt, wenn der Bereich verlassen wird, egal wie.

Dies gilt jedoch nur für Anweisungen, die sich im finally-Block befinden (die bei Verwendung nicht explizit festgelegt werden können using). Daher würde in Ihrem Beispiel scope.Complete()niemals aufgerufen werden (ich erwarte jedoch, dass der Compiler Sie vor nicht erreichbarem Code warnt).

Lucero
quelle
2

Im Allgemeinen ist es ein guter Ansatz. Aber in Ihrem Fall, wenn Sie zurückkehren, bevor Sie das anrufenscope.Complete() , wird das TransactionScope nur in den Papierkorb verschoben. Kommt auf dein Design an.

In diesem Beispiel wird Complete () nicht aufgerufen, und der Bereich wird entsorgt, vorausgesetzt, er erbt die IDisposable-Schnittstelle.

M. Mennan Kara
quelle
Es muss IDisposable implementieren oder nicht kompilieren.
Chriseyre2000
2

scope.Complete sollte unbedingt vorher aufgerufen werden return. Der Compiler zeigt eine Warnung an und dieser Code wird niemals aufgerufen.

In Bezug auf sich returnselbst - ja, es ist sicher, es in usingAnweisung zu nennen . Die Verwendung wird übersetzt, um zu versuchen, endlich hinter den Kulissen zu blockieren, und schließlich ist Block sicher auszuführen.

Tony Kh
quelle
1

In dem von Ihnen angegebenen Beispiel liegt ein Problem vor. scope.Complete()wird nie genannt. Zweitens ist es keine gute Praxis, returnAnweisungen in usingAnweisungen zu verwenden. Beachten Sie Folgendes:

using(var scope = new TransactionScope())
{
    //have some logic here
    return scope;      
}

In diesem einfachen Beispiel geht es darum, dass; der Wert vonscope ist null, wenn die Verwendung der Anweisung beendet ist.

Es ist also besser, nicht mit Anweisungen nach innen zurückzukehren.

Daryal
quelle
1
Nur weil 'return scope' sinnlos ist, bedeutet dies nicht, dass die return-Anweisung falsch ist.
Preet Sangha
Nur weil Sie keine Best Practices verwenden, bedeutet dies nicht, dass Sie etwas falsch gemacht haben. Dies impliziert, dass es am besten ist, dies zu vermeiden, da dies zu unvorhergesehenen Konsequenzen führen kann.
Daryal
Der Wert scopewird nicht null sein - das einzige , was passieren wird ist , dass Dispose()auf diese Instanz geltend gemacht worden sind, und daher die Instanz sollte nicht mehr verwendet werden (aber es ist nicht null und es gibt nichts zu verhindern Sie , um zu versuchen und zu verwenden das entsorgte Objekt, obwohl dies tatsächlich eine unangemessene Verwendung eines Einwegobjekts ist).
Lucero
Lucero ist ganz richtig. Einwegobjekte sind nach der Entsorgung nicht null. Die IsDisposed-Eigenschaft ist true. Wenn Sie jedoch gegen null prüfen, erhalten Sie false und geben return scopeeinen Verweis auf dieses Objekt zurück. Auf diese Weise verhindern Sie, dass der GC das entsorgte Objekt bereinigt, wenn Sie diese Referenz bei der Rückgabe zuweisen.
ThunderGr
1

Um sicherzustellen, dass das scope.Complete()aufgerufen wird, wickeln Sie es mit ein try/finally. Das disposewird aufgerufen, weil Sie es mit dem usingalternativen try/finallyBlock umbrochen haben .

using(var scope = new TransactionScope())
{
  try
  {
  // my core logic
  return true; // if condition met else
  return false;
  }
  finally
  {
   scope.Complete();
  }
}
Aristos
quelle
Ich denke du willst sagen Wenn du gewonnen hast - Wenn du willst, wirst du nicht ... gemäß deinem Code. :)
0

In diesem Beispiel wird scope.Complete () niemals ausgeführt. Der Befehl return bereinigt jedoch alles, was auf dem Stapel zugewiesen ist. Der GC kümmert sich um alles, was nicht referenziert ist. Es gibt also kein Problem, es sei denn, es gibt ein Objekt, das vom GC nicht aufgenommen werden kann.

ThunderGr
quelle