Adı üstende! Temel veri tipleri aslında programlamanın değil ama onu öğrenmenin temeli sayılabilir. Haliyle teorik binlerce bilgiyi her yerden zaten bulabilirsiniz. "3-4 tane İngilizce terim, ne var bunda sende canım!" diyerek Object Oriented kodlamaya geçebilirsiniz. Zamanında ben de dahil, çoğu yazılımcının bu hataya düştüğünü gördüğüm için böyle bir yazı karalamak istedim. Aslında daha çocuk yaşta bile cebirdeki denklem ve eşitsizliklere giriş ile beraber başımıza musallat edilen X'e karşılık gelir diyebiliriz. Hani bağımlısı, bağımsızı olan; 2 bilinmeyenli denklemlerin meşhur bilinmeyeni, hayranı olduğum büyük üstat Ömer Hayyam'ın bize hediyesi olan X. Bilgisayarlarda ilk kullanıldığı zamanlarda kavram olarak da buna benzer bir kavram iken, günümüzde belleklerin farklı bölgelere ayrılması ile veriye daha hızlı erişim ve belleğinin etkin kullanımı amacı ile kullanımı daha da geliştirilmiştir. Ama ben teknik anlamından ziyade olası en basit şekliyle kafanızda canlandırmaya çalışacağım. Yoksa teknik olarak çok daha ayrıntılı bir konudur ve özellikle, işler karmaşıklaşmaya başladığı veya kaynak tüketiminin hayati önem taşıdığı projelerde eğer gereken önemi göstermezsiniz, üzerine çıkacağınız katlar ilerde temelin çökmesine neden olacaktır. Haliyle bu makale kod yazmada acele edenler için değildir, evde denemeyiniz! smiley

Şimdi bir hesap makinesinde çeşitli işlemler yaptığımızı düşünelim. Elde ettiğiniz sayıyı başka işlemlerde kullanmak için hafızaya almak istiyorsunuz diyelim. Bu sayıyı makinenin hafızasına kaydetmek için M+ tuşuna basarsınız. En kaba tabiriyle değişken; hafızaya almak için bastığımız bu tuşun işlevini görür. Hesaplamaya devam edelim. Sonraki işlem de bir sonuç doğuracak; sonrasında bir tane, bir tane daha... Aslında hesap makinesi adı üstünde gerçek bir "compute" aracıdır ama hoppala 'hesapsayar'ımız çuvalladı başka bir sayıyı hafızaya alamıyor. Şimdi biz ona tekrar veri gireceğiz ve onun bize bir 'bilgi' daha üretmesi yani hesapsayarımızın bilgi-sayara dönüşmesi gerekiyor. Tabi bu bilgi insan algısındaki bilgi ile aynı değil düşünürken basitliği elden bırakmayın! İşte yaptığımız işlemlerle birlikte aynı zamanda rastgele erişimli hafıza(ram) dediğimiz hadiseye ihtiyacımız doğdu. Yani programlama dillerinde hesap makinesindeki gibi tek değişkenlik bir sınır olması söz konusu olamaz. Bizler için bunun bir sınırı yoktur. 'Yoktur'dan kastım günümüzdeki ram kapasiteleri ile değişkenlerin ramde kapladıkları boyutlar göz önüne alınırsa neredeyse yoktur. Bizim M+ tuşumuzdan birçok sayıda bulunur. Ayrıca hesap makinesinde tek bir değeri tuttuğumuz için buna isim vermeye gerek olmasa da biz değişkeni hafızaya çıkartırken bir isim verir ve o değişkene ulaşmamız gerektiğinde bu isim üzerinden ulaşırız. Yani değişken isminin amacı bir nevi hesap makinesindeki MR tuşuna benzer.

Yine mesela yemeğimizi yedik, üzerine şöyle demli bir çay alacağız diyelim. Çayı içebilmek için doldurduğumuz bardak bir nevi değişkene benzetilebilir. Ortada, bir şekilde ihtiyacımız kadar olan çayı demlikten alabilme problemi var. Bu problemi ortadan kaldırmak için bardağa koyup istediğimiz yere taşıma çözümünü getirmiş oluyoruz. Çayı içtikten sonra yıkayıp bardağı kaldırırsınız, yani böyle farz ediyorum. smiley İşte değişkenler de bu şekilde ihtiyaç duyulan veri kadar tanımlanır, ister tanımlandığında ister daha sonra içi doldurulur daha sonra bellek tıkanmalarını önlemek için işi bittikten sonra kaldırılmalıdır. Aslında donanıma müdahale etmediğiniz bir uygulamada artık bunları frameworkler arkada sessiz sedasız halledebiliyorlar. Örneğin C#'a özel olarak işi bittikten sonra çöp toplayıcı (garbage collector) sistemi sayesinde yok edilirler. Yani C# sizin yerinize bulaşıklarınızı toplayıp ortalığı siler süpürür ona o yüzden şu an bir torpil yapıyorum.wink Tabi bunu kısmi olarak manuel hale getirebildiğiniz bir mekanizma da yine dil içinde bırakılmış durumdadır. 

Aslında şu an çok kaba tabirlerle kafanızda canlandırmaya çalışıyorum, yalnız introduction bölümünü bitirip nesne yönelime başlayan birisi bu benzetmemde kafa karışıklığı yaşayabilir. Eğer değişken konusuna henüz başladıysanız bu paragrafın devamını atlayın, yok nesne yönelime yeni başladı iseniz paragrafa devam edebilirsiniz. İşin aslı; bardağın kendisi gerçek hayatta da kendine has rengi, boyutları vb. özellikleri olan bir nesnedir. Örneğin beyaz renk, 5 cm. çapında, 10 cm. yüksekliğinde bir bardak diyelim. Bu tanımlarda kullandığımız "beyaz renk, 5cm. çap, 10 cm. yükseklik" birer değişkene karşılık gelir. Bardağı bu şekilde ayrıştırırsanız temeline inmiş olursunuz. Yani temeli derken atomaltı da demedik, işte uygulamalarımızdaki modellemeler için ihtiyacımız olan kadar temeli. "Temel Veri Tipleri" denmesinin sebebi de budur. Nasıl ki matematikte rakamlar olmadan hiçbir işlem yapamazsak makine dili hariç yazılım dillerinde de temel veri tipleri olmadan bir iş yapamazsınız. Hatta bu örneğim üzerinden gidersek, işler arka planda daha da ayrıntılı ya da daha doğru bir ifade ile değişken tanımlama mekanizması çok daha özgür bir kullanım sağlar. Şöyle bir evde yaşadığınızı düşünün: tüm hammaddeleri ve bu hammaddeleri ihtiyacınız olduğunda işleyerek size gereken eşyayı istediğiniz fonksiyonellikte ortaya çıkaran makinelerin, hatta o eşya ile işiniz bittiğinde yine aynı hammaddeye çeviren bir geri dönüşüm sisteminin olduğu bir ev. Yukarıdaki bardağa ihtiyacınız var diyelim. Değişkenler ile bardağın özelliklerini tanımlayarak bardağınız önünüze gelir, üstelik constructor ile isterseniz içinde içeçeği de hazır ya da rengi, boyutları size özel belirlenmiş bir şekilde. Ayrıca bu özellikler .NET içinde eğer sınıf kapsamında kullanılırsa field, kontrollü bir şekilde değer girilmesi isteniyorsa aynen İngilizce karşılığında olduğu gibi property diye geçer ki şahsen kod okunurluğu bakımından C#'ı birçok dilden avantajlı hale getirdiğini düşünürüm. Bunu sağlayan OOP ilkelerinden encapsulation dediğimiz ilkedir. Bu bardak mutfağın dışında başka bir yerde kullanılmasın gibi bir isteğimiz olursa da erişim belirleyici dediğimiz anahtar kelimeler devreye girer. Bu bardak ailede büyükler kullanabilsin ama çocuklar kullanamasın derseniz kalıtım ile bunu sağlayabilirsiniz. Ama aynı bardağı büyükler farklı, küçükler farklı hareket ya da özelliklerde kullansın isterseniz polymorphism, su içen kişi içme esnasında farklı hareketler de yapmaya zorunlu olsun derseniz interface, içerken tek bir sözünüzle sizin istediğiniz sırada hareketleri yapsın gibi tuhaf bir isteğiniz olursa delegate tipini kullanırsınız. Sizin istediğiniz hareketler dışında, içen kişi kendi hareketlerini belirlemek isterse tanımladığınız delegate tiplerinden uygun olanını kullanarak bir event oluşturur ya da kendi oluşturduğu delegate tipi üzerinden event oluşturup bunu yapabilir. Bir tık öteye gideyim derseniz, mesela yukarıda bahsettiğim kalıtım örneğini generic sınıflar ile çok daha esnek ve özgür bir mimari sağlayabilirsiniz. Yalnız değişkenin ne olduğunu öğrenmek isteyenler, yukarıdaki uyarıma rağmen eğer bu paragrafa devam ettilerse çoktan pişman olmuşlardır bile. smiley Çünkü bu bahsettiklerim nesne yönelimli programlama ile ilgilidir. Bu konu böyle bir paragrafta bahsedilebilecek kadar basit değil tabi ama ben ana hatlarıyla girmek için bunları yazdım. Hatta tek başına OOP konseptini düzgün kurdurmak için SOLID, Design Patterns, Aspect Oriented Programming gibi prensip ve yaklaşımlar da dünyada kabul görmüştür. Bunlar benim rastgele bulduğum örnekler, bu küçük örneklerden sonra şimdi konuya biraz daha teknik açıdan yaklaşalım.

Her şeyden önce "Neden farklı veri tipleri ile çalışmaya ihtiyaç duyarız?" sorusuna bakalım. Çünkü farklı veri tiplerinin ramde kaplayacakları alan ve onlara yaptıracağımız işlemler farklıdır. Örneğin tamsayılar, metinsel ifadeler, mantıksal ifadeler ilk akla gelenlerdir. Mesela tamsayılarla dört işlem yaptırabiliyorken, metinsel ifadeler ile bu mümkün değildir. O nedenle her programlama dilinde farklı işlemler için farklı veri tipleri bulunur. Bu şekilde hem ramde ayrılacak yerden israf etmemiş, hem de sadece o veri tipi ile yapılabilecek işlemlerin yapılmasına izin vermiş oluruz. Ayrıca bir yerlerde avantajları olarak rastlayabileceğiniz üzere bazı diller tip güvenli ya da tip denetimli bir dildir. Nedir bu tip güvenliği? Bu güvenlik, tip uyumu için tüm işlemlerin derleyici tarafından tip kontrolündeng eçirileceği anlamına gelir. Kural dışı olan işlemler derlenmez ve derleme zamanında hata verirler. Böylece hataları önlemeye yardımcı olarak kodunuzun güvenilirliği artırılmış olur. Bu yüzden 'tipi olmayan bir değişken' gibi bir kavram örneğimizdeki C# için söz konusu değildir. Bir değişken bildirimi yapılırken o değişkenin veri tipi kesinlikle bildirilmek zorundadır. Sonrasında da o değişkene atanan veriler, belirtilen veri tipinin sınırları ve tipi dışına çıkamaz. Bunun bir istisnası, bir değişkenin tuttuğu verinin başka bir tipe dönüştürülmesi(casting) işlemidir. Bu veri tipi tarafında sadece küçücük bir örnek ama bu disiplinin sağladığı (generic architecture, reflecting, refactoring) gibi pek çok avantaj vardır. Fakat örneğin web uygulamarında başka alternatifiniz olmayan JavaScript object oriented olarak bilinse de aslında object based bir dildir. Yani nesne yönelimli değil nesne tabanlı ve nesne yönelimin sadece temellerini alan ve bahsettiğim kısıtlamaların olmadığı bir dildir. Bu yüzden kendisiyle şahsen pek anlaşamayız. Bu denetime alışana kadar ne gerek var bu kadar çileye dersiniz ama alıştıktan sonra öyle bir ararsınız ki günün birinde TypeScript diye bir teknoloji çıkıverir. Her neyse, iyice konu dışına çıkmayayım ama şunu söyliyebilirim bu dillerin veri tipleri farklı olsa da yaptıkları iş tamamen aynıdır.

C# 4.0 ile tip denetiminin derleme anında değil, çalışma zamanında yapılmasını sağlayan dynamic adlı yeni bir veri tipi getirilmiştir. Derleme anında gerçekleştirilen normal tip denetimi için bir istisnadır.

Derleme anında değişkenin tipini C# derleyicisinin bularak atamasını yapan "var" anahtar kelimesi aynı zamanda tipin belli olmadığı anonymous tiplerde kullanılır. Bu kullanım daha çok veritabanı, XML gibi veri kümelerinden veri çekerken gelecek tipin belirlenmesinin güç olduğu ya da hiç belirlenemediği durumlarda kullanılır. Hatta daha performanslı olduğu için 3.parti ReSharper gibi IDE araçları bu durumu her tip için özellikle tavsiye ederler.

Değişken nedir?

Değişken teknik anlamda ise ramde belirli tipten bir veriyi tutması için ayrılan adrestir. Program içinde üretilen değerleri ramde geçici olarak saklamak amacı ile kullanılırlar. Her değişkene, ramde o değişkenin tutacağı veri tipine karşılık gelecek kadar bir yer ayrılır. Bu yere "değişkenin adresi" denir. Adresi olduğu gibi her değişkenin bir adı da vardır. Bu ad kullanılarak, değişkene değer atanabilir, atanan bu değer tekrar okunabilir ve atanan değer değiştirilebilir. Bir değişkene erişebilmek demek bu 3 isteği yerine getirebilme yeteneklerine sahip olmaktır. Aslında bundan "yetenek" diye bahsedince ekstra bir özellik kazandırılmış gibi bir şey oluşmasın kafanızda. Değişkene erişilip erişilemeyeceği yetkisini belirlemekten bahsediyorum. Yanı sıra, bazen yazdığımız kodların işlevselliği ve güvenliği açısından farklı yerlerden ulaşılamamasını belirleyen çeşitli anahtar kelimeler kullanırız. Fakat bunlardan bahsetmek için henüz erken.

Değer Tipleri ve Referans Tipleri

Aslında yukarıda özellikle C# diye bahsettiğim kısımlar hariç bütün dillerde aynıdır fakat anlatımda somut bir dil kullanılacaksa ben C# tercih ediyorum çünkü ayrıntısıyla bildiğimi düşündüğüm dil. Bu yüzden somut veri tiplerini onda gösterip konuyu kapatıyorum. C#'da veri tipleri, değer tipi ve referans tipi olmak üzere iki genel kategoride toplanır. Bu iki tip arasındaki fark, bir değişkenin ne içerdiği ile ilgilidir. Değer tipinde bir değişken 10 ya da 10,05 gibi gerçek bir değer içerirken; referans tipi bir değişken, o değere yapılan referansı içerir. Yani ne demek istiyorum? Değer tipleri belleğin stack(yığın) bölümünde çalışırken referans tipleri heap(öbek) bölümünde çalışır. Basit kalmaya çalışıyorum, eğer biraz daha derine ineyim derseniz belleğin bu bölümlerini araştırabilirsiniz. Aşağıda göstereceğim int, decimal gibi tipler mesela C#'da tanımlanan primitive struct tiplerdir ve referans tipe nazaran müthiş hızlı çalışırlar ama ihtiyacı karşılamadığı durumlarda bu tiplerden kendimiz bir referans tip oluştururuz ve bu tip çağrıldığında bir nevi paketlenip bütün olarak önümüze geldiği için daha yavaş çalışır. Ama hız dışındaki asıl büyük fark; değer tipleri veri aktarımında kopyalama yaparken, referans tiplerden oluşturulan örneklerin tümü ram üzerinde tek bir adresi işaret ya da referans eder. Bu yüzden birinde yapılan değişiklik tümünü etkileyecektir. Eğer bunu gerektiği gibi kavramayazsanız tehlikeli sularda geziyorsunuz demektir! Öncelikle değer tipini ele alalım.

C#'ın temelinde 13 adet veri tipi yer alır. Bu değer tiplerine primitif basit tip(primitive type) de denir. Her zaman tek bir değer içerdikleri için basit olarak nitelendirilmişlerdir. Başka bir deyişle, iki ya da daha farklı değerin bir araya gelmesinden oluşmazlar. C# tip sisteminin temelini oluşturur ve bir programın kullandığı en temel veri tiplerini sağlarlar.

Basit tiplere ek olarak Enumaration(numaralandırma) ve strucrute(yapı) olmak üzere iki ayrı veri tipi daha tanımlıdır.

Bu temel tipler aşağıdaki tablolarda görülüyor. Bu değerleri ezberlemeye kalkmayın, zira en amatör yazılımcının dahi, değer aralıklarını biliyorum diye övünecek kadar kendini komik duruma düşüreceğini sanmıyorum. Yoksa sanıyor olabilir miyim? (!) Muhtemelen olmaz diyorsunuzdur ama duyduğum için söylüyorum, olurda bir yerlerde bu değerleri size soran olursa koşarak uzaklaşın. Vaktinizi harcadığınıza değmez. Zaten ben de MSDN'den kopyalayıp üzerinde küçük düzenlemeler yaptım. Sadece isimlerini ve genel hatlarıyla hangisini nerede kullanmanız gerektiğini bilseniz yeterli. Gördüğünüz gibi bazıları tamsayılar, bazıları kayan noktalı sayılar, karakterler, mantıksal ifadeler... Bunun gibi ayrıntılara sonraki yazılarımda devam etmeyi planlıyorum. Şimdilik benden bu kadar.

Değer Tipleri
Adı .Net Framework(CTS) Karşılığı Açıklama Aralık
sbyte System.Byte 8 bit işaretli tam sayı [-128, 127]
short System.Int16 16 bit işaretli tam sayı [-32.768, 32.767]
int System.Int32 32 bit işaretli tam sayı [-2.147.483.648, 2.147.483.647]
long System.Int64 64 bit işaretli tam sayı [-9.223.372.036.854.775.808, 9.223.372.036.854.775.807]
byte System.Byte 8 bit işaretsiz tam sayı [0, 255]
ushort System.UInt16 16 bit işaretsiz tam sayı [0, 65.535]
uint System.UInt32 32 bit işaretsiz tam sayı [0, 4.294.967.295]
ulong System.UInt64 64 bit işaretsiz tam sayı [0, 18.446.744.073.709.551.615]
float System.Single 32 bit tek kayan sayı(7 basamak) ± 1,5*10-45, ± 3.4*1038~
double Sytem.Double 64 bit çift kayan sayı (15-16 basamak) ± 5*10-324, ± 1.7*10308 ~
decimal System.Decimal 128 bit ondalıklı sayı (28-29 basamak) ± 1,5*10-28, ± 7,9*1028~
bool System.Boolean 8 bit true ya da false
char System.Char 16 bit Bütün unicode karakterleri
Referans Tipleri
Adı .Net Framework(CTS) Karşılığı Açıklama
object System.Object Bütün veri türlerinin türediği sınıf
string System.String Unicode karakterlerinden oluşan string