C# Bilinçli Tür Dönüşümü

By Burak TUNGUT - 21.11.2012 - Kategori C#

Bu makalemiz de Bilinçli Tür Dönüşümleri konusunu, checked ve unchecked anahtar kelimeleriyle tanışacağız.
Bilinçsiz Tür Dönüşümleri hakkında yazdığımız makaleye ve yaptığımız örneklere buradan ulaşabilirsiniz.

Explicit yani Bilinçli tür dönüşümleri, genellikle derleyicinin ve IDE'nin izin vermediği durumlarda kullandığımız bir yapıdır ve olması muhtemel veri kayıpları söz konusu olduğu için ise son derece dikkatli bir şekilde yapılmaları gerekir.
Tıpkı bir önceki konuda olduğu gibi bilinçli tür dönüşümlerinide Büyükten Küçüğe ve Küçükten Büyüğe dönüşüm şeklinde ikiye ayırabiliriz fakat, Büyükten Küçüğe yapılan tür dönüşümleri bilinçsiz tür dönüşümleri ile tıpatıp aynı olduğu için bu konu üzerinde fazla vakit harcamayacağız.

Öncelikle tür dönüşümü yapmak için kullancağımız syntax yapısını inceleyelim,

            [DönüştürülecekTip] [DeğişkenAdı];
            (int) sayi;
            (byte) _byte;
            (decimal) _ondalik;

Bir önceki konuda yaptığımız tipik dönüşümü tekrarlayalım ve yine bir byte veriyi integer veriye çevirelim,
 

namespace Makale.Test
{
    class Program
    {
        static void Main(string[] args)
        {
            byte _byte = 350;
            int _int = (byte)_byte;

            Console.WriteLine(_int);
        }
    }
}


Ekran çıktımız 50 şeklinde olacaktır. Burada değinmek istediğim bir konu var. Bir önceki makaledede aynı örneği yaptık ama (byte) gibi bir ifade yani bilinçli tür dönüşüm ifadesi kullanmadık ve yine aynı sonucu elde ettik çünkü byte veri türü, int veri türünden daha küçüktü ve dönüşümünde herhangi bir sakınca yoktu.

Peki ne değişti ? Ya da neden bu yapıyı kullandık ?

Aslında değişen hiç birşey yok, arka planda taşlar yine aynı şekilde yerlerine oturtuldu.
Buradaki amacımız sadece uygulamadaki kodların okunabilirliğini arttırıp, yazan kişinin haricinde biri bu koda baktığında burdaki amacımızı anlaması!

Bir düşünsenize orada bilinçsizce bir dönüşüm yapmış olsaydık bir başka ekip arkadaşınız kodda belkide bir hata yaptığınızı düşünebilirdi çünkü byte bir veriyi dönüşüm operatörü kullanmadan int bir değere aktarmak çokta mantıklı değil :)
 

Oluşabilecek Veri Kayıpları

Evet şimdi geldik konunun en fazla can alıcı noktasına gelelim. Bir önceki konumuzdan hatırladığımız gibi bilinçsiz olarak büyük türlerin küçük türlere olan dönüşümleri derleyici tarafından veri kaybı yaşanmaması için engellenmiştir.
Bilinçli tür dönüşümünde tıpkı isminden de kaynaklandığı gibi derleyiciye, "biz bunun dönüşmesini istiyoruz!" şeklinde bir diretme yapıyor ve oluşabilecek veri kayıplarınında farkında olduğumuzu bir nevi bildiriyoruz.
 

namespace Makale.Test
{
    class Program
    {
        static void Main(string[] args)
        {
            int _int = 50;
            byte _byte = (byte)_int;

            Console.WriteLine(_byte);
        }
    }
}

Bu sefer yukarıdaki örneğini tam tersini yapıyor ve 50 değerini taşıyan bir int değişkenini daha küçük veri taşıyabilen byte değişkenine atıyoruz. Uygulamayı çalıştırdığımızda hiç bir veri kaybı olmadan 50 değerinin taşındığını ve ekrana yazdırıldığını göreceğiz.

Şimdi ise işin bir adım daha derinine ve matematik tarafına inelim ve büyük türün küçük türe nasıl dönüştüğünü inceleyelim,
_int yukarıda da gördüğümüz üzere 4byte veri taşıyabilen bir primitif olarak deklare edildi ve içine 50 sayısı yazıldı.
(Ön bilgi olarak 1byte = 8bit olduğunu hatırlatalım)

Öncelikle 4byte yani 32bit veri değerine sahip olan int primitifinin ramdaki görünüşünü aşağıdaki gibi inceleyelim. Görüldüğü gibi 4 tane 8 bitlik bloktan oluşan primitifimiz deklare edildiğinde bu halde oluyor.

int => 00000000 00000000 00000000 00000000

Şimdi ise _int = 50 dememiz ile oluşan değişikliği göz önüne getirelim,
 

_int => 00000000 00000000 00000000 00110010 = 50(10'luk tabanda)

Aslında yaptığımız sadece 50 sayısını ikilik (binary) tabanda yazmak oldu)

1byte = 8bit eşitliğinden bahsetmiştik, e aslında parmak hesabı bile yaparsak 50 sayısının 2'lik tabandaki değeri 110010 idi ve halen 255 olan byte primitifinin alan sınırları dahilindeydi.
Kısacası 255 yani 1 byte değer tipinin alabileceği maksimum değeri aşmadık ve tür dönüşümümüz veri kayıpsız tamamlandı.

Sonuç olarak _byte değişkenimizin RAM'deki görünümü, anlamlı 3 adet 1'er bytelik alanın gitmesiyle aşağıdaki gibi oldu ;

_byte=> 00110010 = 50


Peki şimdide 50 yerine 350 değerini yazarak sonucun nasıl olacağını gözlemleyelim,

 

 

namespace Makale.Test
{
    class Program
    {
        static void Main(string[] args)
        {
            int _int = 256;
            byte _byte = (byte)_int;

            Console.WriteLine(_byte);
        }
    }
}


Uygulamayı çalıştıracak olursak 94 gibi çok alakasız ve ilginç bir cevap alacağız. Aslında şu anda veri kaybı yaşadağımızı farketmiş olmamız gerekir :)
Dilerseniz hemen RAM'deki görüntülerimizi irdeleyelim,

 

 

 

 

_int => 00000000 00000000 00000001 01011110 = 350(10'luk tabanda)

Dikkatinizi kırmızıyla işaretlediğim son bloğa çekmek istiyorum çünkü ne olursa olsun 1byte = 8bit olacaktır ve bu değerin bir byte primitifini aktarılması halinde sadece son 8bitlik kısım aktarılcaktır ve,

 

 

 

_byte=> 01011110 = _int = 94

Şeklinde olacaktır. Byte değerimiz rejime girmişcesine ağırlıklarını attı ve karşımıza 94 olarak geri döndü :)

 

 

 

Checked anahtar sözcüğü

Biraz önce büyük türlerin küçük türlere nasıl dönüştürüldüğünü ve oluşabilecek veri kayıplarından bahsettik fakat bu oluşabilecek veri kayıplarının önüne nasıl geçebileceğimizin daha doğrusu nasıl farkedileceğinden hiç bahsetmedik. Evet yukarıda yazdığımız 3 satırlık örnekte bariz bir şekilde veri kaybının ortaya çıkacağı belliydi fakat büyük ve kurumsal uygulamalarda birbirlerinden karışık değişkenlerin havalarda uçuştuğu ve tür dönüşümlerinin hangi birine saldıracağına şaşırdığı durumlarda ( evet biraz savaş alanına benzetip durumu dramatize etmiş olabilirim laugh ) ne yapacağız ?
İşte bu durumda yani runtime anında uygulamanın bize hata fırlatmasını checked anahtar sözcüğü ile sağlayabiliriz.

 

 

 

 

namespace Makale.Test
{
    class Program
    {
        static void Main(string[] args)
        {
            int _int = 350;
            byte _byte;
            checked
            {
                _byte = (byte)_int;
            }

            Console.WriteLine(_byte);
        }
    }
}

En son yaptığımız uygulamayı şimdide böyle deneyelim. Yaptığımız tek farklılık tür dönüşümünün yapılacağı satırı checked anahtar sözcüğünün baş gösterdiği bloklar arasına almak oldu.
Dikkat! Deklare işlemini bloklar içersinde yapmanız halinde, bloklar dışarsından değişkeninize ulaşmanız mümkün olmayacaktır!
Uygulamayı çalıştırdığımız da ise,

Overflow Expection : Arithmetic operation resulted in an overflow.
Şeklinde bir hata fırlaması söz konusu olacaktır ki tam olarakda aslında istediğimiz bu olacak.
Aslında uygulamaya hata fırlatması için de zorlamamız bir hayli ilginç laugh

 

 

 

 

 

Unchecked anahtar sözcüğü

Genel bir tabir ve giriş seviyesi İngilizce seviyesi ile unchecked anahtar sözcüğünün yukarıda işlediğimiz checked anahtar sözcüğünün tam tersi olduğunu anlayabiliriz :)

Aslında C# da yaptığımız tüm kod blokları default olarak unchecked durumdadır, yani normal bloklar ile unchecked bloklar arasında hiç bir fark bulunmamaktadır Unchecked anahtar kelimesinin dahilindeki kod bloklarını da sadece checked kod blokları içersinde kullanmamızın tek nedenide budur. Yani uzun uzuna yazdığımız checked kod blokları içersinde zaman zaman da unchecked kod yazmak istediğimizde bu anahtar kelimeyi kullanabiliriz.

Bunun içinde şöyle bir örnek yapalım,

 

 

 

 

namespace Makale.Test
{
    class Program
    {
        static void Main(string[] args)
        {
            int _int = 350;
            byte _byte, _byte2;
            checked
            {
                unchecked
                {
                    _byte2 = (byte)_int;
                }
                _byte = (byte)_int;
            }

            Console.WriteLine(_byte);
            Console.WriteLine(_byte2);
        }
    }
}

_byte2 değerine hata fırlatılmadan veri kaybı yaşanarak veri aktarımı gerçekleşirken, _byte değerine aktarılma sırasında oluşabilecek veri kaybından dolayı hata alacağız.

Bugünde güzel bir konuyla yazımızı bitirdik. Açıkçası bu yazıyı yazarken bir hayli keyif aldım ve konunun derinine indikçe, 2 dakikada kitaptan okunarak geçilecek bir konu olmadığı kanaatine bir kez daha vardım.
Konunun şimdi dışarsında çıkmış olacağım ama yazdığım makaleleri eleştiren sayın İlkay İlknur hocamdan bugün nasıl bir eleştiri alacağım çok merak ediyorum laugh İşin garibi eleştiri aldıkça hoşnut oluyorum bunu huyumu ne yapacağım acaba laugh

Bir sonraki makalemde görüşmek üzere, esen kalınız...

 

Yorum Bırak

Facebook
Son Yorumlar