C# Günlüğüm — Sayı #1

Merhabalar.

Okumayı, yazmayı ve paylaşmayı oldukça seviyorum. C# Günlüğüm serisinde de okuduğum veya izlediğim içeriklerden kendimce ilginç bulduklarımı paylaşıcam.

.NET’ in Bileşenleri

.NET, kodu önce IL’ ye derler ve bu IL kodu çalıştırılmak istendiği zaman .NET CLR(Common Language Runtime), JIT(Just In Time) derleyicilerini kullanarak makine diline çevirir.

CLR(Common Language Runtime)

.NET altyapısında programların çalışmasını kontrol eden ve işletim sistemi ile programımız arasında yer alan birimdir.

Platformdan bağımsız bir geliştirme ve yürütme ortamı istiyorsak CLR devreye girer.

IL(Intermediate Language)

.NET’ te kodumuzu derleyip elde ettiğimiz IL kodu işlemciye bağımlı bir kod değildir. IL, taşınabilir ve daha geneldir. IL içinde, değişken tanımları, değişkenlerin nasıl saklanacağı, metotların nasıl çalıştırılacağı, aritmetik işlemler, mantıksal işlemler, bellek kullanımı ve hata yakalama ve yönetimi dahil birçok işin nasıl yapılacağı açıklanır.

Bunların yanında IL’ de Metadata olarak adlandırılan bir birim daha vardır. Metadata, programda kullanılan verilerin tiplerinin yanında oluşturduğumuz sınıfların metotlarını ve bunların özelliklerini ve diğer bilgilerini içerir. Metadata’ nın içeriği çalışma zamanında JIT derleyicileri tarafından kullanılır.

JIT(Just In Time)

C# derleyicisi ile IL’ e derlediğimiz programı çalıştırırken JIT derleyicileri devreye girerler. Bu derleyiciler programın çalıştırıldığı sistemin ve işlemcinin anlayabileceği makina kodunu oluştururlar.

Not: code-reuse = kodun tekrar kullanılabilirliği

Araştırma: Normal JIT, Pre-JIT, Eco-JIT

.NET programlarının derlenip çalıştırılması

CTS(Common Type System)

.NET’ in CTS özelliklerinden doğan, programlama dillerinin kullandığı veri türleri arasında uyumluluk vardır. Mesela, C# dilini kullanarak geliştirdiğimiz bir programdaki int tipindeki değişken ile C++.NET ve VB.NET’ teki tamsayı tiplerinin kapasiteleri aynıdır.

CLS(Common Language Specification)

.NET platformunda diller arası uyumluluğu sağlamak için sadece veri tiplerinin uyumlu olması yetmeyecektir. Program kodunu yazdığımız dilin CLS uyumlu olması şartı aranır. Yani CLS’ ye uyan bir dille yazdığımız kodla diller arası etkileşimi sağlarız.

Assembly

Burada üzerinde duracağımız Assembly kavramının, düşük düzey bir dil olan Assembly dili ile bir alakası yoktur. Sadece ortada bir isim benzerliği vardır. .NET platformu için yazacağımız bütün kodların sonucunda oluşan exe ve dll(dynamic link library) uzantılı dosyalara genel olarak assembly denmektedir.

Projemize ait derlenmiş kodlar ve metadata diye adlandırdığımız özniteleyeci kodlar Assembly içinde bulunurlar.

Assembly’ lerin özellikleri;

  1. Assembly’ lerin içinde yer alan metadata olarak adlandırılan veriler, Assembly’ deki tür bilgilerini ve başka kaynaklarla olan bağlantırlarını saklarlar.
  2. Assembly’ de exe yada dll versiyon bilgisi tutulur. Eskiden klasik dll ve exe tipi dosyalarda versiyon bilgisi saklanmadığı için çeşitli uyumsuzluklar yaşanabilmekteydi. Mesela, farklı iki firmanın hazırladığı dll’ ler aynı isimli olduğunda sonradan register edilen dll, halihazırda bulunan dll’ in üzerine yazıldığı için sistemde bulunan bazı uygulamalarda sorun çıkıyordu. Dll’ ler bu tür sorunlara yol açtığı için DLL HELL kavramı ortaya çıkmıştı.
  3. Assembly’ lerde versiyon bilgisi saklandığı için bir uygulama içinde farklı versiyonlara sahip Assembly’leri kullanabiliriz.
  4. Program kurma işlemi, Assembly’ ye ilişkin dosyayı direkt kopyalayarak yapılabilir.

Application Domain

Assembly’ lerin en önemli özelliklerinden biri de, Application Domain dediğimiz kavramdır. Application Domain sayesinde aynı anda çalışan birden fazla program veya proses birbirinden izole edildiği halde sistemde herhangi bir aksaklığa yol açmadan aralarında veri alışverişi yapabilir.

İsim Alanları ve .NET Sınıf Kütüphanesi

.NET’ teki sınıf kütüphaneleri bir dilden bağımsızdır.

İsim alanları(namespace) .NET framework sınıf kütüphanesindeki veri türleri ve C# dilinde using anahtar sözcüğü ile birlikte kullanılır ve derleyiciye bildirilir. Programlarımızı yazarken birbiriyle iligili olarak geliştirdiğimiz sınıfların aynı isim alanı içinde olması program yazma, programdaki hataları bulma ve programımıza yeni özellikler ekleme aşamalarında büyük fayda sağlar.

System

.NET ile çalışırken gerekli temel sınıfları içerir. Ayrıca diğer tüm sınıf kütüphaneleri de bunun içinde kümelenmiştir.

System.Data

Tüm veritabanı işlemleri için hazır gelen sınıf kütüphanelerine System.Data isim alanından erişilir.

System.Xml

Veri biçimlendirme ve internetten veri paylaşımı XML verileri ile çalışmak ve onları etkin bir biçimde .NET mimarisinde kullanabilmek için geliştirilmiştir.

System.Net

Dağıtık uygulama geliştirme projeleri için iyi bir çözüm aracı olan .NET mimarisinde, başta HTTP protokolü olmak üzere, ağ programlama için ihtiyacımız olan tüm sınıflara buradan erişip hızlıca program geliştirmemiz mümkün.

System.IO

Dosyalardan veri okumak ve yazmak için elimizin altında bulunan System.IO isim alanı sayesinde çok fazla hata çıkabilecek I/O işlemlerini daha rahat bir şekilde halletmemizi sağlamıştır.

Not: C#’ ta nesne olmayan hiçbir şey yoktur.


Stack Bölgesi

Genel anlamda stack bölgesi dediğimizde RAM’ i anlarız. Programımızın içinde basit bir tamsayı türünden nesnenin tipik olarak çalışma zamanında yüklendiği yer RAM’ in stack dediğimiz bölgeleridir.

Stack bölgelerine mikroişlemcilerde bulunan stack pointer vasıtası ile doğrudan erişilebilir. Stack pointer o anda bellekte çalışılan bölgenin adresini tutan yapıdır. Stack pointer, bellekteki alan tahsisatına göre bir azaltılır yada bir arttırılır. Bu yüzden stack bölgesinde tutulacak verilerin çalışma zamanı öncesi ne kadar alan kapladığının bilinmesi gerekir. Aksi bir durumda stack pointer işlevi ile çelişkili bir durum olurdu .NET platformunun altyapısını oluşturan JIT derleyicilerinin de programı yüklediğinde stack pointer’ı doğru konumlandırmak için tahsisatını yapacağı verinin tam boyutunu bilmesi gerekir.

Last in — First out

Bütün verilere eğer stack bölgesinde tahsisat yapılmış olsaydı, programılarımızın esnekliği azalırdı; Zira C#’ ta bazı nesneler referans yolu ile belirtildikleri için verinin kendisi stack’ ta bulunmayabilir.

Heap Bölgesi

Stack bölgelerinde olduğu gibi heap alanları da RAM’ de bulunan hafıza alanlarıdır. Bütün C# nesneleri bu bölgede oluşturulur. Stack’ tan farklı olarak heap bölgesinde tahsisatı yapılacak nesnenin derleyici tarafından bilinmesi zorunlu değildir. Bu yüzden, heap bölgesini kullanmak programlarımıza büyük esneklik katmaktadır. C#’ ta heap bölgesinde bir nesneye alan tahsisatı yapmak için new anahtar kelimesi kullanılır. New anahtar kelimesi ile tahsisatı yapılmış veriler çalışma zamanında dinamik olarak yaratılırlar yani derleme zamanında veriler için herhangi bir tahsisat yapılmaz. Heap alanının bize sunduğu esneklik avantajının yanı sıra bazı dezavantajları da vardır. Bunlardan en önemlisi işlemlerin hızının stack mekanizmasına göre daha yavaş olmasıdır.

Register Bölgesi

Stack ve Heap allocation mekanizmalarına göre çok hızlıdır. Sebebi ise Register bölgesinin ikincil bir bellekte olmamasıdır. Register’ lar mikroişlemcinin içinde bulunan sınırlı sayıdaki yapılardır.

Register sayıları sınırlı olduğu için derleyiciler çok sık işlem yaptıkları verileri hız kazanmak için register’ larda tutarlar. Bizim mikroişlemci register’ larına doğrudan erişim hakkımız yoktur.

Statik Bölge

Bellekteki herhangi sabit bir bölgeyi temsil eder. Static alanlarda tutulan veriler programın bütün çalışma süresince saklanırlar. C# ile bir nesneye statik özelliği vermek için static anahtar sözcüğü kullanılır.

Sabit Bölge

Constant değerler genellikle program kodlarının içine gömülü şekildedir. Bu değerlerin değişmesi mümkün değildir. Sadece okuma amaçlı oldukları için hızlılık açısından bazı durumlarda ROM(Read Only Memory)’ de tutulurlar.

Ram Olmayan Bölge

Bellek bölgesini temsil etmeyen disk alanlarını temsil eder. Bazı veri türlerinin kalıcı olması istenir. Bu durumda yukarıda saydığım herhangi bir bölgedeki verilerin bir kısmı program sonlandığında disklere kaydedilir. Kısacası program çalışmadığında da verinin bulunmasını istiyorsak bu tür disk sistemleri kullanmalıyız.

Not: Constant ifadeler içsel tasarım olarak zaten statik oldukları için ayrıca statik olarak belirtmek hatalıdır.


Value ve Reference Tipleri

Değişkenler, bellekte tutulan verilerdir. Aslında bir değişkeni kullanırken o değişkenin bellekte tutulduğu adresteki veriye ulaşıyoruz. Value tipleri değişkenin değerini direkt bölgesinden alır. Reference tipleri ise başka bir nesneye referans olarak kullanırlar. Yani reference tipleri aslında bir çeşit bellek bölgesi olan heap alanında yaratılan veri türlerinin adreslerini saklarlar.

Value tipleri yaratıldıklarında stack dediğim bellek bölgelerinde oluşturulurlar, reference tipleri kullanımı biraz daha sınırlı olan heap dediğimiz bellek bölgesinde saklanırlar.

C# dilinde kullanıcın direkt olarak kullanabileceği bir pointer veri türü tanımlaması yoktur. Bunun yerine bazı değişkenler value tipleri bazıları ise reference tipleri olarak işlem görürler.

Value: Temel veri tipleri olan int, double, float ve struct nesneleri gibi veri türleri value tipleridir.

Reference: Herhangi bir sınıf türü ise reference tipidir.

C# Data Types

İki value tipi nesnesini birbirine eşitlerken değişkenlerde saklanan değerler kopyalanarak eşitlenir ve bu durumda iki yeni bağımsız nesne elde edilmiş olur yani birinin değerini değiştirmek diğerini etkilemez, ancak iki reference tipini birbirlerine eşitlediğimizde bu nesnelerde tutulan veriler kopyalanmaz, işlem yapılan nesnelerin heap bölgesindeki adresleridir, yani iki nesnede aslında heap bölgesinde aynı adresi gösterecekleri için birinde yapılan değişiklik diğerini de etkileyecektir. Reference tiplerini tanımlarken herhangi bir adresi göstermediğini belirtmek için null değerler atanır.

Not: Değer tiplerinin tamamı Object denilen bir nesneden türemiştir. Yani her nesne yada veri tipi aslında bir object tipidir.

Not: Bütün değer tiplerinin constructor işlevleri vardır.

Struct: Yapı nesnesindeki bütün değer tipleri varsayılan değere, reference tipleri ise null değere atanır.

.net type

Sabit ayıraçları: C# 7.0 versiyonuyla beraber sayı tipindeki değişkenleri daha okunaklı hale getirmek için “_” karakterini ayıraç olarak kullanabiliriz.

int i = 1_000_000;

Not: Bir dilin syntax açısından özel anlamlar ifade eden karakterleri kullanmak istiyorsak bunlar \(escape) ifadesiyle belirtmemiz gerekir. Mesela bir dizin bilgisini içeren bir string nesnesinin aşağıdaki gibi tanımlarız.

string yol = “C:\\xxxx\\”;

Bu tür kullanıma “escape sequence” kullanımı denir.

Bu tür kullanımın yerine string içinde görünen ifadenin aynısını belirtmek için string ifadesinin önüne @ işareti konulur. Mesela;

string yol = @“C:\xxxx\”;

Object: C#’ ta bütün veri türlerinin gizli olarak türediği veri türüdür.

var: C# 3.0 ile birlikte bir metot içerisinde tanımlanacak değişkenler tipleri bildirilmeden tanımlanabilmektedir.

var i = 4;

Not: Dile eklenen var anahtar sözcüğü sayesinde değişkene atanan değerin tipinden çıkarsama(type inference) yapılarak derleme aşamasında IL içerisine gerçek tip bildirimi eklenir. Yani yukarıdaki tanımlama biçimi ile aşağıdaki tanımlama biçimi arasında çalışma zamanı açısından hiç bir fark yoktur.

int i = 4;

Not: Bir değişkeni object olarak tanımlamak ile var bildirimi ile tanımlama arasında hiç bir benzerlik yoktur. Çünkü object ile tanımlanan değişkenlerin tür zamanında dinamik olarak yapılır. var ile bildirimde ise bu işlem derleme aşamasında yapılır.

Şimdilik bu kadar yeterli bir sonraki bölümde görüşmek dileğiyle.