Tip Dönüşümü

Bundan yaklaşık 4 ay önce halen çalışmakta olduğum işime henüz yeni girmiş ve geliştirilen yazılımları inceleyip anlamaya çalışıyordum ki karşılaştığım bir kod parçası beni hayretler içinde bırakmıştı. Yazılımcı arkadaşımızın C#’da TryParse isimli bir metodun varlığından haberi olmasa gerek söz konusu metodu kendisi yazmış ve bunu da pek çok yerde kullanmıştı. Veri tipleri ve tipler arasındaki dönüşüm C#’da en temel konular arasında olsa da çoğu zaman özellikle de yazılıma yeni başlayanlar tarafından göz ardı ediliyor ve ortaya karmaşık yapılar, düşük performanslı yazılımlar çıkıyor.

Değişkenler bir programlama dilinde temel verileri saklamak ve bu verileri sonradan kullanmak için kullanılan bellek bölgeleridir. C# dilinde genel olarak bir değişken tanımlaması aşağıdaki gibi olmaktadır.

<veri tipi> <veri adı>;

C# dilinde temel olarak veri tipleri ikiye ayrılır. Bunlar önceden tanımlanmış veri tipleri ve kullanıcı tarafından tanımlanmış veri tipleridir. Önceden tanımlanmış veri tipleri de kendi arasında referans tipi (reference types) ve değer tipi (value type) olmak üzere ikiye ayrılır.

Değer tipli değişkenlerin boyutu programın derlenmesi aşamasında bellidir ve bellekte stack (yığın) adı verilen bölgede doğrudan verinin kendisi tutulur. Referans tipli değişkenlerin ise boyutu programın derlenmesi aşamasında belli değildir. Bu sebeple veri stack üzerinde değil yine bellekte heap adı verilen başka bir bölgede tutulur. Stack üzerinde ise, veriye işaret eden (adres bilgilerini tutan) bir değer tipi saklanır.

Değer tipli değişkenlerde, değişkene ilk değer atanmadığında değişken otomatik olarak ilk değerini belirler. Örneğin int değişkenine 0, bool değişkenine false şeklinde atama yapılır. Referans tipli değişkenler oluşturulduğunda ve ilk değer ataması yapılmadığında ise değeri otomatik olarak null olur. Değer tipli bir değişken başka bir değer tipli değişkene eşitlendiğinde Stack bölgesinde bir veri kopyalama işlemi oluşur. Fakat referans tipli değişkenlerde, aynı veriyi gösteren farklı bir referans oluşturulur.

C# tip-korumalı bir dildir. Başka bir deyişle C# dili programda kullanılacak değişkenlerin tutacağı veri tiplerini kesin sınırlarla birbirinden ayırır. Dolayısıyla, bir değişken bildirimi yapılırken, o değişkenin veri tipi kesinlikle belirtilir. Daha sonra o değişkene atanan veriler, belirtilen veri tipinin dışına çıkamaz.

C# Tipi .NET Tipi Uzunluk(Byte) Değer Aralığı
byte Byte 1 0 – 255
sbyte SByte 1 -128 – 127
short Int16 2 -32768 – 32767
ushort UInt16 2 0 – 65535
int Int32 4 -2147483648 – 2147483648
uint UInt32 4 0 – 4294967295
long Int64 8 -1020 – 1020
ulong UInt64 8 0 – 2×1020
float Single 4 ±1.5 x 10-45 – ±3.4×1038
double Double 8 ±5.0×10-324 – ±1.7×10308
decimal Decimal 16 ±1.5×10-28 – ±7.9×1028
char Char 2
bool Boolean 1 true, false

Bir değişkenin tuttuğu verinin başka bir veri tipine dönüştürülmesine casting (çevrim) denir. C# dilinde çevirme işlemi sadece belirli özellikteki veri tipleri arasında yapılabilmektedir. Bunlar;

  • Sayısal veri tipleri
  • Kalıtım hiyerarşisi içindeki sınıflar
  • Aynı arayüzü uygulayan sınıflar
  • Farklı ama birbirleri için dönüşüm operatörleri tanımlanmış sınıflar

Çevrim işlemi, implicit (bilinçsiz) ve explicit (bilinçli) olmak üzere 2 farklı şekilde yapılabilir. C# ta genel olarak veri kaybına yol açma olasılığı olmayan dönüştürmeler (bellekte daha küçük bir alan kaplayan veri tipinden daha büyük bir alan kaplayan veri tipine dönüştürme) derleyici tarafından implicit olarak gerçekleştirilir.

int a  = 123;
long b;
b = a;  // b = 123

Implicit olarak dönüştürme yapılabilecek veri tipleri aşağıdaki tabloda listelenmiştir.

Kaynak Hedef
sbyte short, int, float, long, double, decimal
byte short, ushort, int, uint, long, ulong, float, double, decimal
short int, long, float, double, decimal
ushort int, uint, long, ulong, float, double, decimal
int long, float, double, decimal
uint long, ulong, float, double, decimal
long, ulong float, double, decimal
char ushort, int, uint, long, ulong, float, double, decimal
float double

Diğer taraftan veri kaybına yol açma olasılığı olan dönüştürmeler (bellekte daha büyük bir alan kaplayan veri tipinden daha küçük bir alan kaplayan veri tipine dönüştürme) tür dönüştürme operatörü kullanılarak explicit olarak yapılabilmektedir. Bu tür dönüştürmeler tür dönüştürme operatörü kullanılmadan yani implicit olarak yapılırsa hata ile karşılaşılır.

int a;

long b = 456;

a = b;        // HATA

a = (int)b;   // a = 456

double c = 12.34;
a = (int)c;   // a = 12

Checked ve Unchecked Blokları

Explicit dönüştürmede dikkat edilecek önemli bir husus dönüştürme sonrası veri kaybının olabileceğidir. Derleyici varsayılan olarak bu durumda hata üretmez. Veri kaybı olduğunda programın hata vermesi istenen kısımlar checked blokları içine alınır. Benzer şekilde veri kaybı olduğunda programın hata vermesi istenmeyen kısımlar ise unchecked (varsayılan durum) blokları içine alınır.

int i=256;
int a=300;
byte b, c;

checked{
   b=(byte)i;
   unchecked{
      c=(byte)a;
   }
}

Tip Dönüştürme Operatörleri

Implicit ve Explicit olarak birbirine dönüşümü mümkün olmayan iki sınıfın birbirine dönüşümünü mümkün kılmak için tip dönüştürme operatörleri kullanılır.

public class Inch{
   public decimal Length { get; set; }
   public static implicit operator Cm(Inch i){
      Cm c = new Cm();
      c.Length = i.Length * 2.54M;
      return c;
   }
}

public class Cm{
   public decimal Length { get; set; }
   public static explicit operator Inch(Cm c){
      Inch i = new Inch();
      i.Length = c.Length / 2.54M;
      return i;
   }
}

Inch i1 = new Inch();
i1.Length = 5;
Cm cm1 = i1;          // Implicit casting

Cm cm2 = new Cm();
cm2.Length = 3;
Inch i2 = (Inch)cm2;  // Explicit casting

Boxing ve Unboxing

Değer tipinden bir değişkenin, object türüne implicit dönüştürülmesine boxing işlemi denir. Boxing işleminin tam tersi olan, objet türünden bir değişkenin, değer tipine explicit dönüştürülmesine ise unboxing işlemi denir.

int i1 = 123;

object o1 = i1;   // boxing

int i2 = (int)o1; // unboxing

Is ve As Operatörleri

Boxing ve Unboxing işlemleri veya sıradan bir tip dönümüşümü yapılırken değişkenin tipini dönüşüm yapılmak istenen tiple is operatörü ile karşılaştırarak çevrimin doğru olup olmadığı kontrol edilebilir.

DerivedType derivedObj = new DerivedType();

if(derivedObj is BaseType){
   BaseType baseObj = (BaseType)derivedObj;
}

As operatörü is operatöründen farklı olarak kontrol işlemini dönüştürme işleminden önce değil sonra yapılmasını sağlar. As operatörü ile yapılan dönüştürme işleminde doğru olmayan bir dönüşüm yapıldıysa hata üretilmez ancak değişken null değerini alır.

DerivedType derivedObj = new DerivedType();
BaseType baseObj = derivedObj as BaseType;
if(baseObj != null){

}

GetType ve typeof

.NET içinde herhangi bir veri tipi ile işlem yapmak için kullanılabilecek Type isimli sınıf bulunmaktadır.  Type sınıfından bir nesne elde etmek için ise başlıca iki yöntem vardır: GetType metodu ve typeof operatörü.

GetType metodu .NET’de her yapının doğrudan ya da dolaylı olarak türediği object sınıfı içinde tanımlanmış ve dolayısıyla her yapıya miras olarak aktarılmış bir metodtur. typeof operatörü ise parametre olarak aldığı tür adından türe ilişkin ilgili Type nesnesini oluşturur. Aradaki en büyük fark GetType nesneler üzerinde çalışırken, typeof sınıflar üzerinde çalışır.

MyClass c = new MyClass();

Type t1 = c.GetType();
Type t2 = typeof(MyClass);

Console.WriteLine(t1 == t2);

Int32.Parse

String tipindeki bir değişkeni int tipine dönüştürmek için kullanılan static  Int32.Parse metodunun 3 adet Exception’ı bulunur. Bunlar ArgumentNullException, FormatException ve OverflowException‘dır.

  • ArgumentNullException: Giriş değeri null olduğu zaman gerçekleşir.
  • FormatException: Giriş değeri doğru formatta olmadığı zaman gerçekleşir. Örneğin; alfanümerik bir değer gibi.
  • OverflowException: Giriş değeri Int32 sınırlarını aştığında gerçekleşir.
string metnim1 = null;
string metnim2 = "123.456";
string metnim3 = "12345678910111213141516";
string metnim4 = "123456";
int sonuc;

sonuc = Int32.Parse(metnim1); // sonuc = ArgumentNullException
sonuc = Int32.Parse(metnim2); // sonuc = FormatException
sonuc = Int32.Parse(metnim3); // sonuc = OverflowException
sonuc = Int32.Parse(metnim4); // sonuc = 123456

Convert.ToInt32

Convert.ToInt32 metodunun Int32.Parse’tan en önemli farkı, yalnızca string verileri Int32 türüne çevirmek değil, bunların dışındaki veri tiplerini de çevirebiliyor olması geliyor.

İkinci önemli fark ise Int32.Parse’ın aksine 2 exception’a sahip olması. Bunlar FormatException ve OverflowException‘dır. Convert.ToInt32 giriş verisinin null olması durumunda hata fırlatmak yerine geriye 0 değeri döndürür.

string metnim1 = null;
string metnim2 = "123.456";
string metnim3 = "12345678910111213141516";
string metnim4 = "123456";
int sonuc;

sonuc = Convert.ToInt32(metnim1); // sonuc = 0
sonuc = Convert.ToInt32(metnim2); // sonuc = FormatException
sonuc = Convert.ToInt32(metnim3); // sonuc = OverflowException
sonuc = Convert.ToInt32(metnim4); // sonuc = 123456

Int32.TryParse

Verilen string değer int tipine dönüştürülebiliyorsa geriye true değeri dönülerek işlem gerçekleştirilir. Aksi halde hata fırlatılmaz sonuç olarak 0 üretilir ve geriye false değer dönülür. Bu sebeple riskleri en aza indirdiğinden diğer dönüştürme metodlarına göre daha avantajlıdır.

string metnim1 = null;
string metnim2 = "123.456";
string metnim3 = "12345678910111213141516";
string metnim4 = "123456";
int sonuc;

bool durum = Int32.TryParse(metnim1, out sonuc); // sonuc = 0, durum = false
bool durum = Int32.TryParse(metnim2, out sonuc); // sonuc = 0, durum = false
bool durum = Int32.TryParse(metnim3, out sonuc); // sonuc = 0, durum = false
bool durum = Int32.TryParse(metnim4, out sonuc); // sonuc = 123456, durum = true

Tip Dönüştürme Kuralları

  1. Tamsayı türlerinden tamsayı türlerine tür dönüştürme operatörü ile dönüşüm yapılırken gerçekleşecek olay, checked ya da uncheckeddurumda olup olmamaya göre değişir.
    1. Eğer checked durum içerisinde bulunuluyorsa, programın çalışma zamanı sırasında kontrol yapılır. Dönüştürülecek türdeki değer, dönüştürmenin yapıldığı türün sınırları içerisinde değilse, System.OverflowException türünden hata fırlatılır.
    2. Eğer unchecked durum içerinde bulunuluyorsa, sayınının yüksek anlamlı bitleri atılacak biçimde dönüştürme yapılır. Eğer, büyük işaretli türden küçük işaretsiz türe dönüştürme söz konusuysa, yüksek anlamlı bitler atıldıktan sonra kalan kısım işaretli olarak yorumlanır. Eğer küçük işaretli türden büyük işaretsiz türe dönüştürme yapılıyorsa, büyük ürün yüksek anlamlı bitleri işaret bitiyle doldurulur ve elde edilen değer işaretsiz olarak yorumlanır.
  2. decimal türden diğer tamsayı türlerine dönüştürme yaparken,sayının noktadan sonraki kısmı atılır. Elde edilen tam kısım, hedef türün sınırları içerisine sığmıyorsa, ister checked isterse unchecked durumda olunsun System.OverflowException oluşur.
  3. float ya da double türünden tamsayı türlerine operatör ile yapılan dönüştürmede, oluşacak durum checked ya da uncheckeddurumunda olmaya göre değişir.
    1. Eğer checked durumda bulunuluyorsa, noktadan sonraki kısım atıldıktan sonra oluşan değer, hedef türün sınırları içerisinde kalıyorsa işlem başarı ile yürütülür. Fakat oktalı kısım atıldıktan sonra oluşan değer hedef türün sınırları içerisine sığmıyorsa System.OverflowException oluşur.
    2. Eğer unchecked durumda bulunuluyorsa, ve noktadan sonraki kısım atıldıktan sonra hedef türün sınırları içerisinde kalınıyorsa problem oluşmaz. Fakat eğer oluşan deeğr hedef türün sınırları içerisinde kalmıyorsa, dönüştürmeden hangi değerin elde edileceği belirsiz (unspecified) bırakılmıştır.
  4. double türden float türüne dönüştürme yapıldığında hata, mantis kısmında ya da magnitute kısmında oluşabilir. Eğer mantis kısmında bir hata oluyorsa duyarlılık en yakın float duyarlılıkla ifade edilir. Eğer magnitute hatası oluşuyorsa dönüştürmeden duruma göre pozitif ya da negatif sonsuz elde edilir.
  5. float ve double türden decimal türüne yapılan dönüştürmelerde eğer bir magnitute hatası söz klonusu ise System.OverflowException oluşur, eğer mantis hatası söz konusu ise decimal türüne en uygun ifade dönüştürme sonucu elde edilir.
  6. decimal türden float ve double türüne dönüştürme yapıldığında sayı, en yakın float ya da double sayı ile temsil edilir. Fakat eğer bir magnitute hatası söz konusu olursa System.OverflowException oluşur.
Reklamlar

2 Responses to Tip Dönüşümü

  1. haydutx says:

    Oldukça kapsamlı bir yazı olmuş. Elinize sağlık

  2. ahmet says:

    süper bir yazı tşklerr

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google fotoğrafı

Google hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s

%d blogcu bunu beğendi: