Tür Dönüşümü Nedir?
Veri tipleri arasında çeşitli dönüşümler vardır. Farklı türden değişkenlerin aynı ifade içinde işlem görmeleri için tür dönüşümü kullanılır.Sayısal değerler içeren veri tipleriyle sözel değerler içeren veri tipleri birbirlerine dönüştürülebilir. Burada basit bir mantık vardır. bazı hocaların bu konuya yaklaşımı “Küçük kap, büyük kabın içerisine olur ancak tam tersi mümkün değildir” şeklinde olurken sn. Sefer Algan bu konuya "…nasıl 3 elma ile 2 armudun toplamı mantığınıza ters düşüyorsa aynı şekilde 3 byte ile 2 int'in toplamı da bilgisayar açısından mantıksızdır…"şeklinde yorum getirmiştir .kısaca tip dönüşümlerinde ki temel mantık budur.
Derleyici tarafından bir değişkeni tanımladığımız türün dışında geçici olarak başka bir türe çevirmeye bilinçsiz tür dönüşümü denir.
DİPNOT: Bilinçsiz yapılan tür dönüşümlerinde bir nesnenin türü asla kalıcı olarak değiştirilmez.
Tür dönüşümünü ikiye ayırdığımız gibi,bilinçsiz tür dönüşümünü de küçük türün büyük türe,büyük türün küçük türe dönüştürülmesi şeklinde ikiye ayırabiliriz.
Küçük Türün Büyük Türe Dönüştürülmesi
Küçük türler , veri kaybı yaşanmaksızın sorunsuzca kendinden büyük türlere dönüştürülebilirler. Dönüşüm sırasında ise veri tipine göre değişecek olan büyüklüğümüz de sonradan eklenecek olan bitler 0 olarak kalırlar.
DİPNOT: Bazı türler arasında tür dönüşümü yapmak mümkün değildir. Bunlar :
- Bool, decimal ve double türünden herhangi bir türe
- Herhangi bir türden char türüne
- Float ve decimal türünden herhangi bir türe (float türünden double türüne dönüşüm hariç) bilinçsizce dönüştürülemez.
Büyük Türün Küçük Türe Dönüştürülmesi
Büyük türlerin küçük türlere otomatik dönüştürülmesi C#’da yasaklanmıştır. Eğer bu tür bir dönüştürme(bilinçsiz olarak) mümkün olsaydı birtakım veri kayıpları yaşanacaktır.
DİPNOT: Bu tür dönüşümün mümkün olmaması C# dilinin tip güvenliğine (type safety)ne kadar önem verdiğinin bi göstergesidir.
[using System; public class Tur_donusumu { static void Main() { byte a=20; int b; b=a; Console.WriteLine(b) float f=20f; double d; Console.WriteLine(d); char c='a' ; decimal m; m=c; Console.WriteLine(m); } }]
Bilinçli Tür Dönüşümü
Bilinçli tür dönüşümleri genelde C#'ın izin vermediği dönüşümleri sağlamak için yapılır.
Bu tür dönüşümlerde de yine küçük türler büyük türe ya da tersi dönüşümler yapılabilir.Küçük türlerin büyük türlere çevrilmesi aynı bilinçsiz dönüşümde olduğu gibidir.O yüzden bu bölümde ondan bahsetmiycem.
Tür Dönüştürme Operatörü
Bilinçli tür dönüşümü yapılırken “tür dönüştürme operatörleri” kullanılır. Tür dönüştürme operatörü parantez içinde değişken ya da sabitten önce yazılır.
(dönüştürülecek tür) değişken_yada_sabit;
[using System; class Turdonusumu { static void Main() { int a=5 byte b=(byte) a; Console.WriteLine(b); } }]Örnek üzerinde de gördüğümüz (byte)a ifadesi, a değişkeninin byte halini tuttu.
DİPNOT: Eğer değişken adı kısmında tek bir değişken yoksa, bir ifade varsa parantez içine alınması gerekir.
Bilinçli Tür Dönüşümünün Sakıncaları
Bilinçsiz yapılan tür dönüşümlerinde büyük türler, küçük türlere dönüştürülemiyordu. Eğer tür dönüştürme operatörü kullanılırsa bu işlem mümkün olur.
[using System ; public class Tur_donusumu { static void Main() { int a=400; byte b=(byte)a; Console.WriteLine(b); } }]
Programı çalıştırdığımızda ekrana 144 yazdırdı. Bunun sebebi sizce nedir?
int a=400 :00000000 00000000 00000001 10010000
(byte)i : 10010000 144 Dec.
int a=400 :
(byte)i : 10010000 144 Dec.
[using System; class TurDonusumu { static void Main() { int i = 256; byte b = (byte)i; Console.WriteLine(b); double d = 123456.7890123456; float f = (float)d; int i3 = (int)d; Console.WriteLine(f); Console.WriteLine(i3); decimal m = 123456789.123456789M; double d2 = (double)m; Console.WriteLine(d2); int i2 = 65600; short s = ( ) short i2; Console.WriteLine(s); } }]Gördüğünüz gibi tür dönüştürme operatörü ile yapılan bilinçli tür dönüştürme işleminin faydası kadar zararı da vardır.Bu yüzden programımızdaki hataları en aza indirmek için mümkün oldukça tür dönüştürme işlemlerini kullanmamaya çalışırız.Tabi ki bazı durumlarda tür dönüşümü kullanmak bizim hızlanmamızı sağlayacaktır.Yani tamamen de yok saymayız.
- Bu şekilde tür dönüşümü değişkenlere uygulanabildiği gibi sabitlere de uygulanabilir:
using System;
class Turdonusumu
{
static void Main()
{
byte b=(byte) 12.5f;
Console.WriteLine(b);
}
} - Reel türler tam sayı türlere dönüşürken ondalık kısım atılır.
- Bilinçsiz tür dönüşümüyle yalnızca küçük türler büyük türlere dönüşebiliyordu, yani veri kaybı olması imkansızdı. Halbuki bilinçli tür dönüşümünde veri kaybı gerçekleşebilir.Eğer dönüşümünü yaptığımız değişkenin tuttuğu değer dönüştürülecek türün kapasitesinden büyükse veri kaybı gerçekleşir. Bu gibi durumlar için C#'ın checked ve unchecked adlı iki anahtar sözcüğü vardır.
Herhangi bir zamanda bir değişkenin bir maksimum değerden büyük olup olmayacağını bilemeyiz.Bu yüzden veri kayıplarına karşı önlemler almalıyız.Bu önlemlerden birisi de checked anahtar sözcüğünü kullanmaktır.
Checked anahtar sözcüğü ile çalışma zamanında bu tür veri kayıplarının olabileceği durumlarda hata vermesini sağlarız.Eğer checked anahtar kelimesi kullanılmamış olsaydı herhangi bir hata verilmeden program icrasına devam edecektir.
[using system ; class TurDonuşumu; { static void Main() { int i =256; byte b; checked { b=(byte)i; } console.writeline(b); } }]
Bu hata ile atama işleminde bir taşmanın olduğu gösterilmektedir. Checked anahtar sözcüğünün kullanımıyla ilgili dikkat edilmesi gereken önemli noktalardan biri de checked bloklarının içinde tanımlanan değişkenlerin blok dışında tanınmayacağıdır.Blok faaliyet alanında tanımlanan değişkenler blokların dışında tanınmazlar.Örneğin aşağıdaki program derlenemeyecektir.
[using system ; class TurDonuşumu; { static void main() { int i =256; checked { bayt b =(byte)i; } console.writeline(b); } }]
Programın derleme zamanında vereceği hata şudur:"the name 'b' does not exist in the class or namespace 'turdonusumu' "yani, b değişkeni sınıf yada isim alanında bulunamadı.
Normal şartlarda yapılan işlemler “unchecked”dir. Böyle bir ifadenin konmasının nedeni uzun “checked” blokları içerisinde bazen “unchecked” blokların oluşturulması istenebilir. Bu durumlarda çok fazla blok oluşturmamak için “unchecked” ifadesi kullanılabilir.
[using System; class TurDonusumu { static void Main() { int i1 = 255; int i2 = 500; byte b, c; checked { b = (byte)i1; Console WriteLine(b) unchecked { c = (byte)i2; } Console.WriteLine(c); } } }]
Referans ve Değer Türleri Arasındaki Dönüşüm
C# dilinde tür dönüşümleriyle ilgili en önemli konu değer tipindeki verileri referans tiplerine çevirmektir. Daha önce değer tipleri ile referans tiplerinin bellekte farklı bölgelerde tutulduğunu söylemiştim. Bu iki veri tipinin dönüşümü farklı bellek alanlarından dolayı biraz farklıdır. Bu başlık altında değer tiplerini referans tiplerine nasıl dönüştürebileceğimizi, boxing ve unboxing kavramlarını ve aynı zamanda object türüne ait olan ToString metodu ile değer tipindeki verileri string türüne nasıl dönüştürebileceğimizi görücez.
Object türü ve ToString() Metodu
C# dilinde her şeyin bir nesne olduğunu daha önceden söylemiştim. Nesne olmayan hiçbir şey yoktur. Temel veri türleri de dahil olmak üzere bütün veri tipleri object dediğimiz bir referans türünden türemiştir. Yani temelde tek bir nesne vardır. Diğer nesneler kalıtım yolu ile bu nesneden türeyerek özelleşir ve farklı amaçlarda kullanılır. Türeme, kalıtım yolu ile olduğu için var olan özellikler her zaman korunur. Bu kural C# için tamamen geçerlidir. Sınıf kütüphanesinin ve temel veri türlerinin atası olan object nesnesinin özellikleri ve iş yapan metotları bütün türlerde mevcuttur. Örneğin Object sınıfına ait olan ToString() metodu bütün temel veri türlerinde ve referans türlerinde kullanılabilir.
int i = 20;
string s = i.ToString(); //int türündeki i değişkeninin 20 olan değerini string'e çevir. Artık string oldu.
Boxing
Bir nesnenin object türüne bilinçsiz ya da bilinçli olarak dönüştürülmesidir. Daha önce de denildiği gibi değer tipleri dahil bütün veri türleri object türünden bir değişkene atanabilir. Object nesneler referans tipi olduğu için heap dediğimiz bellek bölgesinde tutulurlar. Halbuki değer tipi nesneler stack dediğimiz bellek bölgesinde tutulurlar. O halde bir değer tipini referans tipinden bir nesneye atadığımız zaman stack bölgesinde tutulan veri, bit olarak heap alanına kopyalanır. Ve stack bölgesindeki object türünden olan değişken de bu heap bölgesini gösterecek şekilde ayarlanır. Bütün bu işlemlere boxing işlemi denmektedir.
int i = 20;
object o = i; //değer tipini object'e aktardık bilinçsiz şekilde. Bilinçli şekilde de yapılır farketmez.
yada...
int i = 20;
object o = (object) i; //Bilinçli bir şekilde yaptık bu kez de object türüne dönüştürülmüş değer unboxing işlemi yapılana kadar heap denilen bellek bölgesinde tutulur.
Unboxing işlemi boxing işleminin tamamen tersidir. Yani heap bölgesindeki bir nesnenin değeri bit olarak stack bölgesine kopyalanır. Böylece referans türünü değer türüne dönüştürmüş oluruz. Bu işleme de unboxing işlemi denilmektedir.
DİPNOT: Unboxing işlemi bilinçli bir şekilde yapılır. Bilinçsiz bir şekilde kesinlikle yapılmaz. Bu yüzden tür dönüştürme operatörü kullanılmalıdır. Unboxing işleminin çalışma zamanında (run-time) hata vermemesi için sağlanması gereken iki önemli koşul vardır. Bunlar:
- Unboxing işlemine tabi tutulacak nesnenin daha önceden boxing işlemine tabi tutulması.
- Boxing işlemine tabi tutulmuş olan bu nesnenin unboxing işlemi sırasında doğru türe dönüştürülmesidir.int i = 20;
object o = (object) i; //ilk önce boxing yaptık. Çünkü unboxing yapamayız.
int j = (int)i; //Tür dönüştürme operatörünü kullanarak unboxing yaptık.
- Boxing ve Unboxing işleminden sonra nesnelerin değerlerinin korunacağı kesindir.
System.Convert Sınıfı ile Tür Dönüşümü
.NET sınıf kütüphanesinde yer alan “Convert” sınıfı string değerleri ve temel veri türlerini birbirine çevirmek için kullanılır. Her bir veri türü için ayrı bir çevrim fonksiyonuna sahiptir.
[using System; class TurDonusumu; { static void Main() { string s1 s2; int i1, i2, t; Console.Write("1.Sayıyı Girin:"); s1 = Console.ReadLine(); Console.Write("2.Sayıyı Girin:"); s2 = Console.ReadLine(); i1 = Convert.ToInt32(s1); i2 = Convert.ToInt32(s2); t = i1 + i2; Console.WriteLine "Toplam = " + t.ToStrin lam = " + t.ToString());]
Dönüşüm işleminin sonucunda anlamlı bir sonuç elde edilemeyeceği durumlarda hata meydana gelir.
[using System; class TurDonusumu; static void Main() { char a= 'a'; bool b = Convert.ToBoolean(a);]