Apakah ini pendekatan yang baik untuk memanggil kembali ke dalam menggunakan pernyataan {}?

93

Saya hanya ingin tahu apakah itu pendekatan yang aman / baik untuk menelepon ke returndalam usingblok.

Misalnya.

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

Kami tahu bahwa penjepit paling keriting terakhir dispose()akan dibatalkan. Tapi apa yang akan terjadi dalam kasus di atas, karena returnmelompati kontrol dari cakupan yang diberikan (AFAIK) ...

  1. Apakah saya scope.Complete()dipanggil?
  2. Dan untuk metode ruang lingkup dispose().
Paolo Moretti
sumber
1
Setelah using{}ruang lingkup selesai, objek yang relevan dibuang, returnakan "merusak" ruang lingkup - sehingga benda akan dibuang seperti yang diharapkan
Shai
4
Ketahuilah bahwa scope.Complete()panggilan Anda tidak akan pernah dipukul dengan sampel yang Anda berikan, jadi transaksi Anda akan selalu dibatalkan.
Andy
Terlepas dari apakah using's dispose()disebut, ketika Anda kembali, fungsi mengandung ini usingblok akan telah kembali dan semuanya milik itu akan menjadi yatim piatu. Jadi bahkan jika scopetidak dibuang "oleh using" (itu akan, seperti yang dijelaskan orang lain) itu akan tetap dibuang karena fungsinya berakhir. Jika C # memiliki gotopernyataan -Apakah Anda sudah selesai tertawa? baik- maka alih-alih kembali Anda bisa gotosetelah penjepit penutup, tanpa kembali. Logikanya, scopemau tetap dibuang, tapi Anda baru saja memasukkan gotoC # jadi siapa yang peduli dengan logika pada tahap itu.
Hebat
C # sudah kebagian .
Jake T.

Jawaban:

146

Sangat aman untuk menelepon ke returndalam usingblok Anda , karena blok yang menggunakan hanyalah sebuah try/finallyblok.

Dalam contoh Anda di atas setelah pengembalian true, ruang lingkup akan dibuang dan nilainya dikembalikan. return false, dan tidakscope.Complete() akan dipanggil. Namun akan dipanggil karena itu berada di dalam blok terakhir.Dispose

Kode Anda pada dasarnya sama dengan ini (jika itu membuatnya lebih mudah untuk dipahami):

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();
}

Perlu diketahui bahwa transaksi Anda tidak akan pernah berhasil karena tidak ada cara scope.Complete()untuk melakukan transaksi tersebut.

Øyvind Bråthen
sumber
13
Anda harus menjelaskan bahwa Dispose akan dipanggil. Jika OP tidak tahu apa yang terjadi using, kemungkinan besar dia tidak tahu apa yang terjadi finally.
Konrad Rudolph
Tidak apa-apa untuk meninggalkan blok penggunaan dengan pengembalian, tetapi dalam kasus TransactionScope Anda mungkin akan mendapatkan masalah dengan pernyataan-penggunaan itu sendiri: blogs.msdn.com/b/florinlazar/archive/2008/05/05/8459994.aspx
thewhiteambit
Dalam pengalaman saya, ini tidak berfungsi dengan SQL Server CLR Assemblies. Ketika saya perlu mengembalikan hasil untuk UDF yang berisi bidang SqlXml yang mereferensikan Objek MemoryStream. Saya mendapatkan " Tidak dapat mengakses objek yang dibuang " dan " Upaya tidak valid untuk memanggil Baca saat aliran ditutup. ", Jadi saya DIPAKSA untuk menulis kode bocor dan melupakan pernyataan penggunaan dalam skenario ini. :( Satu-satunya harapan saya adalah SQL CLR akan menangani pembuangan benda-benda ini. Ini adalah skenario yang unik, tetapi saya pikir saya akan berbagi.
MikeTeeVee
1
@MikeTeeVee - Solusi yang lebih bersih adalah (a) meminta penelepon melakukan using, misalnya using (var callersVar = MyFunc(..)) .., alih - alih menggunakan di dalam "MyFunc" - Maksud saya, penelepon diberi aliran dan bertanggung jawab untuk menutupnya melalui usingatau secara eksplisit, atau (b) minta MyFunc mengekstrak info apa pun yang diperlukan ke objek lain, yang dapat diteruskan kembali dengan aman - maka objek atau aliran data yang mendasarinya dapat dibuang oleh Anda using. Anda tidak perlu menulis kode bocor.
ToolmakerSteve
7

Tidak apa-apa - finallyklausa (yang dilakukan oleh kurung kurawal penutup usingklausa di bawah kap) selalu dijalankan saat ruang lingkup ditinggalkan, tidak peduli bagaimana caranya.

Namun, ini hanya berlaku untuk pernyataan yang ada di blok akhirnya (yang tidak dapat diatur secara eksplisit saat menggunakan using). Oleh karena itu, dalam contoh Anda, scope.Complete()tidak akan pernah dipanggil (saya berharap kompilator memperingatkan Anda tentang kode yang tidak dapat dijangkau).

Lucero
sumber
2

Secara umum, ini adalah pendekatan yang bagus. Namun dalam kasus Anda, jika Anda kembali sebelum memanggil scope.Complete(), itu hanya akan membuang TransactionScope. Tergantung desain Anda.

Jadi, dalam contoh ini, Complete () tidak dipanggil, dan cakupan dibuang, dengan anggapan itu mewarisi antarmuka IDisposable.

M. Mennan Kara
sumber
Itu harus mengimplementasikan IDisposable atau using wont compile.
Chriseyre2000
2

scope.Complete pasti harus dipanggil sebelumnya return. Compiler akan menampilkan peringatan dan kode ini tidak akan pernah dipanggil.

Mengenai returndirinya sendiri - ya, aman untuk menyebutnya usingpernyataan dalam . Menggunakan diterjemahkan untuk mencoba-akhirnya memblokir di belakang layar dan akhirnya memblokir harus dieksekusi.

Tony Kh
sumber
1

Dalam contoh yang Anda berikan, ada masalah; scope.Complete()tidak pernah dipanggil. Kedua, bukanlah praktik yang baik untuk menggunakan returnpernyataan di dalam usingpernyataan. Lihat berikut ini:

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

Dalam contoh sederhana ini, intinya adalah; nilai scopeakan menjadi null saat menggunakan pernyataan selesai.

Jadi lebih baik tidak kembali ke dalam menggunakan pernyataan.

daryal
sumber
1
Hanya karena 'lingkup pengembalian' tidak ada gunanya, itu tidak berarti bahwa pernyataan pengembalian salah.
Preet Sangha
hanya karena tidak menggunakan praktik terbaik tidak berarti Anda telah melakukan kesalahan. Ini menyiratkan, yang terbaik adalah menghindari, karena dapat mengakibatkan konsekuensi yang tidak terduga.
daryal
1
Nilai scopetidak akan null - satu-satunya hal yang akan terjadi adalah bahwa Dispose()akan telah dipanggil pada contoh itu, dan karena itu contoh harus tidak digunakan lagi (tetapi tidak batal dan tidak ada yang mencegah Anda untuk mencoba dan menggunakan objek yang dibuang, meskipun ini memang penggunaan yang tidak tepat dari objek sekali pakai).
Lucero
Lucero benar. Benda sekali pakai tidak kosong setelah dibuang. Properti IsDisposed adalah benar, tetapi jika Anda memeriksa terhadap nol Anda mendapatkan palsu dan return scopemengembalikan referensi ke yang objek. Dengan cara ini, jika Anda menetapkan referensi itu saat kembali, Anda mencegah GC membersihkan objek yang dibuang.
ThunderGr
1

Untuk memastikan bahwa scope.Complete()akan dipanggil, bungkus dengan try/finally. The disposedisebut karena Anda harus membungkusnya dengan usingyang alternatif try/finallyblok.

using(var scope = new TransactionScope())
{
  try
  {
  // my core logic
  return true; // if condition met else
  return false;
  }
  finally
  {
   scope.Complete();
  }
}
Aristos
sumber
Saya pikir Anda ingin mengatakan Jika Anda menang - Jika Anda mau, tidak akan ... sesuai kode Anda. :)
0

Dalam contoh ini, scope.Complete () tidak akan pernah dijalankan. Namun, perintah return akan membersihkan semua yang ditugaskan di tumpukan. GC akan menangani semua yang tidak direferensikan. Jadi, kecuali ada objek yang tidak bisa diambil oleh GC, tidak ada masalah.

ThunderGr
sumber