Bu yazımızda using ifadesi nedir, ne değildir, ne yapar onu inceleyeceğim... Yine daha önce olduğu gibi basit bir kod yazarak konunun daha anlaşılabilir olmasını istiyorum.
şeklinde hiçbir şey yapmayan bir kodumuz olsun. Bu kodu Visual Studio (VS) ile konsol uygulaması olarak derlediğiniz zaman ortaya çıkan exe'nin kodunu .NET Framework IL Disassembler (IL DASM) açıyoruz. Bu şekilde aslında arka planda neler oluyor, kodumuz ne hale geliyor onu görmüş oluyoruz. Şimdi VS'nin bizim kodumuzu dönüştürdüğü kodu bir inceleyelim:
kod biraz karışık görünüyor olabilirama adım adım incelersek o kadar da karışık bir şey yapmadığını görüyoruz. İlk başta IL_0001 numaralı satırda DataTable objemizin constructor'ı çağırılıyor ve bir altındaki satırda oluşan obje dt isimli değişkenimize aktarılıyor. Daha sonra ise bir try-finally bloğu açılıyor. Biz using ifadesinin içerisine hiçbir şey yazmadığımız için try bloğunun içerisinde hiçbir şey yapılmadığını görüyoruz. Finally bloğunda ise bir şeyler oluyor. Orayı incelediğimizde ise ilk başta yaratılan objemizin null olup olmadığının kontrol edildiğini görüyoruz. Eğer null değilse objemizin Dispose() methodunun çağırıldığını görüyoruz.
Peki biz bu kodu using ifadesini kullanmadan yazmaya kalkarsak ne olur? VB aynı kodunu oluşturur mu onu deneyelim şimdi de. C# kodumuz şöyle olmalı:
bu kodu derledikten sonra ortaya çıkan .exe'ye IL DASM ile bakıyoruz.
iki tane fazla üretilen nop komutundan (IL_000b ve IL_001a satırlarında) başka farklılık olmadığını görüyoruz. Nop komutu da aslında bir işlem olmadığından dolayı aslında üretilen iki kodun da birbiri ile aynı olduğunu görüyoruz.
Using ifadesi ile Try-finally bloğu bu şekilde kullanıldığında birbirleriyle tam olarak aynı işlemi yaparlar. Hangisini kullanmak isterseniz kullanabilirsiniz buradan o sonuç çıkıyor. Benim tavsiyem kodun okunabilirliği açısından using kullanmak olacak...
using System.Data; namespace Using_TryFinally { class Deneme { static void Main() { using (DataTable dt = new DataTable()) { } } } }
şeklinde hiçbir şey yapmayan bir kodumuz olsun. Bu kodu Visual Studio (VS) ile konsol uygulaması olarak derlediğiniz zaman ortaya çıkan exe'nin kodunu .NET Framework IL Disassembler (IL DASM) açıyoruz. Bu şekilde aslında arka planda neler oluyor, kodumuz ne hale geliyor onu görmüş oluyoruz. Şimdi VS'nin bizim kodumuzu dönüştürdüğü kodu bir inceleyelim:
.method private hidebysig static void Main() cil managed { .entrypoint // Code size 29 (0x1d) .maxstack 2 .locals init ([0] class [System.Data]System.Data.DataTable dt, [1] bool CS$4$0000) IL_0000: nop IL_0001: newobj instance void [System.Data]System.Data.DataTable::.ctor() IL_0006: stloc.0 .try { IL_0007: nop IL_0008: nop IL_0009: leave.s IL_001b } // end .try finally { IL_000b: ldloc.0 IL_000c: ldnull IL_000d: ceq IL_000f: stloc.1 IL_0010: ldloc.1 IL_0011: brtrue.s IL_001a IL_0013: ldloc.0 IL_0014: callvirt instance void [mscorlib]System.IDisposable::Dispose() IL_0019: nop IL_001a: endfinally } // end handler IL_001b: nop IL_001c: ret } // end of method Deneme::Main
kod biraz karışık görünüyor olabilirama adım adım incelersek o kadar da karışık bir şey yapmadığını görüyoruz. İlk başta IL_0001 numaralı satırda DataTable objemizin constructor'ı çağırılıyor ve bir altındaki satırda oluşan obje dt isimli değişkenimize aktarılıyor. Daha sonra ise bir try-finally bloğu açılıyor. Biz using ifadesinin içerisine hiçbir şey yazmadığımız için try bloğunun içerisinde hiçbir şey yapılmadığını görüyoruz. Finally bloğunda ise bir şeyler oluyor. Orayı incelediğimizde ise ilk başta yaratılan objemizin null olup olmadığının kontrol edildiğini görüyoruz. Eğer null değilse objemizin Dispose() methodunun çağırıldığını görüyoruz.
Peki biz bu kodu using ifadesini kullanmadan yazmaya kalkarsak ne olur? VB aynı kodunu oluşturur mu onu deneyelim şimdi de. C# kodumuz şöyle olmalı:
using System.Data; namespace Using_TryFinally { class Deneme { static void Main() { DataTable dt = new DataTable(); try { } finally { if (dt != null) dt.Dispose(); } } } }
bu kodu derledikten sonra ortaya çıkan .exe'ye IL DASM ile bakıyoruz.
.method private hidebysig static void Main() cil managed { .entrypoint // Code size 31 (0x1f) .maxstack 2 .locals init ([0] class [System.Data]System.Data.DataTable dt, [1] bool CS$4$0000) IL_0000: nop IL_0001: newobj instance void [System.Data]System.Data.DataTable::.ctor() IL_0006: stloc.0 .try { IL_0007: nop IL_0008: nop IL_0009: leave.s IL_001d } // end .try finally { IL_000b: nop IL_000c: ldloc.0 IL_000d: ldnull IL_000e: ceq IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: brtrue.s IL_001b IL_0014: ldloc.0 IL_0015: callvirt instance void [System]System.ComponentModel.MarshalByValueComponent::Dispose() IL_001a: nop IL_001b: nop IL_001c: endfinally } // end handler IL_001d: nop IL_001e: ret } // end of method Deneme::Main
iki tane fazla üretilen nop komutundan (IL_000b ve IL_001a satırlarında) başka farklılık olmadığını görüyoruz. Nop komutu da aslında bir işlem olmadığından dolayı aslında üretilen iki kodun da birbiri ile aynı olduğunu görüyoruz.
Using ifadesi ile Try-finally bloğu bu şekilde kullanıldığında birbirleriyle tam olarak aynı işlemi yaparlar. Hangisini kullanmak isterseniz kullanabilirsiniz buradan o sonuç çıkıyor. Benim tavsiyem kodun okunabilirliği açısından using kullanmak olacak...