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
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;
- 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.
- 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ı.
- Assembly’ lerde versiyon bilgisi saklandığı için bir uygulama içinde farklı versiyonlara sahip Assembly’leri kullanabiliriz.
- 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.
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.
İ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.
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.
Kaynaklar:
Sefer Algan, HER YÖNÜYLE C# , Pusula Yayıncılık, İstanbul, 2003
My passion for programming led me to make my own video tutorials to share what I have learned over the years. You will…www.youtube.com
CBU YZM2105: OOP Ders Materyalleri