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

Berkan Şaşmaz
SDTR
Published in
9 min readJul 28, 2019

Merhabalar.

Bu hafta basit diye gördüğüm konuların aslında büyük resmi görmek adına ne kadar da önemli olduğunu bir kez daha anlamış oldum. Belki de uzun bir süre bu yazımda anlattığım konuları bilinçli olarak projelerimde kullanmayacağım ama Regular Expressions gibi bir konun varlığını bile bilmek düşünce tarzımda önemli değişikliklere gitmemi sağladı.

İsim Alanları (Namespaces)

İsim alanları yazdığımız programlarda mantıksal organizasyonu sağlar. İsim alanları sayesinde isim benzerlikleri bir sorun olmaktan çıkmaktadır. Örneğin .NET sınıf kütüphanesindeki Array sınıfının yanında kendimiz de Array isimli bir sınıf tasarlayabiliriz. C gibi yapısal programlama dillerinde bu şekilde aynı isimli yapılar tanımlanamaz. Bu karmaşıklıkları önleyen elbette ki isim alanlarının oluşturduğu mantıksal organizasyondur. İsim alanları sadece mantıksal bir organizasyon sağlamaktadır. C++ dilindeki include anahtar sözcüğünün kullanımı ile karıştırılmamalıdır. include ile dosyalar fiziksel olarak birbirlerine bağlanırken isim alanları ile sadece mantıksal bir işlem yapılır. Yani isim alanları sadece derleme aşaması ile ilgilidir.

using System;

deyimi ile System isim alanını kullanmak istediğimizi derleyiciye bildiriyorduk.

Temel veri türlerini System isim alanını eklemeden de kullanabiliriz. Çünkü bu temel veri türleri C# derleyicisi tarafından otomatik olarak System isim alanındaki yapılardan türetilir. Örneğin int türünden bir değişken tanımlayabilirken int türünün karşılığı olan Int32 türünden bir nesne tanımlayamayız.

classs AnaSınıf
{
static void Main()
{
//Gecerli kullanim
int a = 50;

//Gecersiz kullanim
Int32 b = 20;
}
}

İkinci tanımlamanın geçerli olabilmesi ya programın başına

using System;

deyiminin eklenmesi gerekir ya da tanımlamayı

System.Int32 b = 20

şeklinde değiştirmemiz gerekir.

Not: İsim alanlarında yalnızca class, delegate, enum, Interface, ya da struct bildirimi yapılabilir.

Dispose(): C# dilinde bir nesnenin kaynakları iki şekilde iade edilebilir. Birincisi Dispose() metodunu kullanmak. Dispose() metodunu işlemek için IDisposable arayüzünü bilmemiz gerekir. İkinci yöntem ise yıkıcı metotlar bildirmektir.

Not: using anahtar sözcüğünün diğer bir kullanımı ise belirlenen bir bloğun sonunda nesnelerin Dispose() metodunu çağırmaktır. using anahtar sözcüğünü bu şekilde kullanabilmek için IDisposable arayüzünün ilgili sınıf tarafından işlenmiş olması gerekir. Aşağıdaki programda using bloğunun sonunda d nesnesinin Dispose() metodu çağrılacaktır.

using System;class Deneme : IDisposable
{
public void Dispose()
{
Console.WriteLine("Dispose() metodu cagrildi");
}
}
class AnaSınıf
{
public static void Main()
{
Deneme d = new Deneme();
using (d)
{
Console.WriteLine("using blogu");
} //d.Dispose() metodu cagrilir.
Console.WriteLine("using blogu disi");
}
}

Çıktı:

using blogu
Dispose() metodu cagrildi
using blogu disi

Gördüğünüz gibi using bloğunun hemen sonunda Dispose() metodu çağrıldı. Aynı işlemi using anahtar sözcüğünü kulanmadan da try-catch-finally deseninin yardımıyla gerçekleştirebiliriz.

İç İçe(nested) Geçmiş İsim Alanları

Büyük çaplı yazılım projelerinde(.NET sınıf kütüphanesi gibi) isim alanlarında farklı isim alanları bildirilebilir. Örneğin System isim alanındaki Collections isim alanı gibi.

External Alias(Harici Takma İsimler)

Farklı isim alanları altında aynı isimli sınıflar tanımlandığında ve her iki isim alanı da using deyimi ile eklendiğinde kod içinden her birine erişebilmek için sınıflara ya da diğer tiplere takma isimler(alias) verebiliyoruz. Böylece referans edilmiş bir tipe takma isimleriyle erişbiliyorduk.

Oluşturulan tiplere takma isimler vererek anlam bütünlüğünü bozmak iyi bir çözüm sayılmaz. Ayrıca sadece tiplere takma isim vermek bütün çakışmalarımızı kısa yoldan çözmeyecektir. C# dilinde dll(dynamic link library) kütüphanelerine(assembly) harici isimler(externel alias) son derece güzel bir çözüm sağlamaktadır.

Not: extern alias anahtar sözcüğü bütün using deyimlerinden önce yazılmak zorundadır.

Harici takma isim verilmiş dll kütüphanelerindeki tip ve isim alanlarına erişmek için “::” operatörü kullanılır. Yani :: operatörü sadece harici takma isim verilmiş kütüphaneler ile birlikte kullanılır.

global Harici Takma İsmi

.NET içerisinde referans verilmiş herhangi bir kütüphaneye siz farkında olmasanız bile otomatik olarak bir takma isim verilir. Bu takma ismin adı global’ dir. Herhangi bir C# programını derlediğinizde System.dll gibi birçok temel kütüphane otomatik olarak referans verildiğinden bu kütüphanelere aynı zamanda otomatik olarak global isminde harici bir takma isim verilir (Tabii ki isterseniz değiştirebilirsiniz).

global isimli harici takma ismine kod içinden

global::System.Math = new global::System.Math();

şeklinde erişmek mümkündür.

Not: global harici takma ismi özellikle referans verilmiş bazı tiplerin başka sınıflarca ezilmesi veya isim benzerliğinden dolayı görülmemesinin engellenmesi durumunda oldukça kullanılışlıdır.

Temel Tür Yapıları

C# temel veri türlerinin aslında System isim alanında bulunan çeşitli yapı türlerini temsil ettiğini söylemiştik. Yani int, bool, char gibi anahtar sözcüklerinin birer takma isimden farkı yoktur.

Her bir temel türe System isim alanında karşılık gelen yapılar verilmiştir.

C# dili diğer dillerden farklı bir tür barındırır. Bu decimal türüdür. decimal türü System isim alanında Decimal yapısı ile temsil edilir. Decimal veri türünün bellekte saklanması diğer türlere nazaran biraz daha karmaşıktır. Bu yüzden decimal veri türleri ile iş yapması daha zordur. Decimal yapısında tamsayı türlerinin içerdiği metotların -CompareTo(), Equals(), ToString(), ve Parse()- yanısıra decimal sayısını diğer bütün türlere dönüştürebilmek için çeşitli statik metotlar bildirilmiştir. Bu metotlar ToDouble(), ToInt16(), ToInt32() gibi sıralanabilir. Bu dönüşüm metotlarının yanısıra iki decimal sayı üzerinde dört işlem yapan metotlar da bildirilmiştir.

static decimal Add(decimal d1, decimal d2) // d1+d1
static decimal Divide(decimal d1, decimal d2) // d1/d1
static decimal Multiply(decimal d1, decimal d2) // d1*d1
static decimal Subtract(decimal d1, decimal d2) // d1-d1
static decimal Remainder(decimal d1, decimal d2) // d1%d1

Garbage Collector(GC)

Daha önce de belirtildiği gibi .NET platformunda bulunan gereksiz bilgi toplayıcısı(garbage colletor) mekanizması, programımızın herhangi bir anında kullanılmayan referansları heap alanında siler. GC mekanizmasının ne zaman devreye gireceği kesin olarak bilinmediği için hangi nesnelerin ne zaman sonlandırılacağı da bilinemez. GC mekanizması bellek alanını optimum tutacak şekilde ayarlar. .Net’ in bize sunduğu GC sınıfı ile istediğimiz bir anda garbage collection mekanizmasınının devreye girmesini isteyebiliriz. Bunun için GC sınıfının static Collect() metodunu aşağıdaki çağırmamız yeterli olacaktır.

GC.Collect();

Programın herhangi bir anında o ana kadar tahsis edilmiş toplam bellek alanının byte cinsinden görmek için GC sınıfının GetTotalMemory(true) metodu kullanılır. Bu metodun geri dönüş değeri byte türündendir. Eğer parametre true olarak girilirse değer döndürülmeden önce GC mekanizması ile anlamsız veriler yok edilir. Eğer parametre false ise hiçbir şey yapılmadan o anki toplam tahsis edilmiş alana geri döndürülür.

Not: Dispose() metodunu ve GC mekanizmasını daha iyi anlamak istiyorsanız bu içeriği okumanızı tavsiye ederim.

Not: Garbage Collector sistemimizi çok mu yavaşlatıyor gibi soruların cevabını da bu içerikte bulabileceğinizi düşünüyorum.

Temel String İşlemleri

C#’ ta string işlemlerinin tamamı System.String sınıfındaki üye özellik ve metotlarla yapılır.

Bir String nesnesi bir kere tanımlandıktan sonra, stringde bulunan herhangi bir karakter bir daha değiştirilemez. Ancak string üzerinde çeşitli metotlar ile işlemler yapıp metotların geri dönüş değerleri yeni string nesnelerine aktarılabilir. Yani stringde bulunan her karakter readonly özelliğine sahiptir.

string b = "berkan";
b[0] = 't';

String içindeki karakterlere ulaşabilmek için indeksleyici tanımlanmıştır. Bu yüzden stringlerle bir karakter dizisi gibi işlem yapmamız mümkündür. Yukarıdaki deyimlerin hata vermesinin sebebi String sınıfında tanımlanan indeksleyicinin sadece get bloğuna sahip olmasıdır. Yani sadece okunabilir bir indeksleyici olmasıdır.

Ayrıca yukarıdaki tanımlama biçimi şu ana kadar gördüğümüz tanımlama biçimidir. Bu şekilde tanımlama ile String sınıfının yapıcı metodu gizlice çağrılmaktadır.

Yazıları Biçimlendirme

Öncelikle yazılar üzerinde biçimlendirme yapabilmemiz için ilgili metodun biçimlendirmeyi destekliyor olması gerekir. Console.WriteLine(), String.Format() ve ToString() metotları biçimlendirmeyi destekleyen metotlardandır.

Console.WriteLine(“{0} numaralı atlet {1}. oldu”, numara, sıraNo);

Burada ekrana yazılacak yazıdaki {0} ifadesine “numara” değişkeninin, {1} ifadesinin yerine de “sıraNo” değişkeninin geleceği bildirilmektedir. Bu değişkenleri istediğimiz kadar arttırabiliriz. Şu ana kadar bilinçli bir şekilde yazı biçimlendirmesi yapmadık. Yazıları biçimlendirmek için,

{degiskenNo, genislik : format}

biçim komutunu kullanmamız gerekir. genislik ve format belirtilmedikçe varsayılan değerler kabul edilir.

genislik ile yazılacak yazının en küçük boyutu belirleniyor. Eğer genislik 0' dan büyükse yazılar sağa dayalı, 0' dan küçükse sola dayalı olarak yazılır.

Console.WriteLine(“{0 : X}”, 155); 

Bu deyim ile ekrana 9B yazılacaktır. Buradaki “X” karakteri 155 sayısını 16' lı sayma sisteminde yazdırılmasını bildiriyor. Her format belirleyicisi için duyarlılık ifadesi de vardır. Bu ifadeler format belirtecinden hemen sonra yazılır. Örneğin “X” belirteci için duyarlılık maximum sayıda karakter anlamında gelmektedir.

Console.WriteLine(“{0 : X4}”, 155);

ifadesi ile ekrana

009B

yazdırılır.

Her format belirleyicisi için bu duyarlılık farklı anlamlara gelmektedir. Aşağıda en sık kullanılan format belirleyicileri mevcuttur.

En sık kullanılan format belirleyicileri

Not: String.Format() metodu ve ToString() metoduna biçimlendirme komutu parametre olarak girilebilir.

Not: Format(), static bir metot olduğundan nesne tanımlamadan metodu kullanabiliriz.

int a = 50;
string str = a.ToString("C3");
Console.WriteLine(str);

Çıktı:

50,000 TL

Özel Biçimlendirme Oluşturma

Standart biçimlendirme komutlarının yanısıra önceden özel karakterler yardımıyla kendi biçimlendirmemizi oluşturabiliriz. Bu özel biçimlendirici karakterler aşağıda verilmiştir.

# → Rakam değerleri için kullanılır.

, → Büyük sayılarda binlikleri ayırmak için kullanılır.

. → Gerçek sayılardaki ondalık kısımlarda kullanılır.

0 → Yazılacak değerin başına ya da sonuna 0 karakteri ekler.

% → Yüzde ifadelerini belirtmek için kullanılır.

Console.WriteLine("1--> {0:#,###}, 15533785);
Console.WriteLine("2--> {0:#,##}, 15533.785);
Console.WriteLine("3--> {0:#%}, 0.25);

Çıktı:

1--> 1.554.785
2--> 15554,79
3--> 25%

Düzenli İfadeler(Regular Expressions)

Düzenli ifadeler değişken sayıda karakter dizilerinden oluşabilecek ancak belirli koşulları sağlayabilen ifadelerdir. Diyelim ki bir text dosyası içinde @ karakteri geçen bütün satırları elde etmek istiyoruz. Burada satırdaki karakterlerin uzunluğu ve ne olduğu önemli değildir; yeter ki @ karakteri olsun. Örneğin milyonlarca e-mail adresi olabilir. Ama bir tane e-mail adresi formatı vardır. Her e-mail adresi mutlaka @ karakteri ve en az bir “.” karakteri içermelidir. Eğer birden fazla nokta varsa noktalardan biri mutlaka @ karakterinden sonra olmalıdır.

C# bu tür düzenli ifadeleri temsil etmek için Regex sınıfı geliştirilmiştir. Regex sınıfı System.Text.RegularExpression isim alanında bulunmaktadır. Bir karakter dizisinin oluşturulan düzenli ifadeye uyup uymadığını belirlemek için ise yine aynı isim alanında bulunan Match isimli sınıftan faydalanılır.

Düzenli İfadelerin Oluşturulması

  1. Bir düzenli ifadenin satır başında mutlaka istenilen bir karakter ile başlaması isteniyorsa ^ karakteri kullanılır. Örneğim ^7 düzenli ifadesinin anlamı yazının mutlaka 9 ile başlaması gerekir.
  2. Belirli karakter gruplarını içermesi istenen düzenli ifadeler için \ karakteri kullanılır. Aşağıda bunlara örnekler verilmiştir.
  • \D ifadesi ile yazının ilgili yerinde rakam olmayan tek bir karakterin bulunması gerektiği belirtilir.
  • \d ifadesi ile yazının ilgili yerinde 0–9 arasında tek bir karakterin bulunması gerektiği ifade edilir.
  • \W ifadesi ile alfanümerik olmayan karakterin olması gerektiği bildiriliyor. Alfanümerik karakterler a-z, A-Z ve 0–9 aralıklarındaki karakterlerdir.
  • \w ile yazıdaki ilgili yerde sadece alfanümerik bir karakterin olabileceği bildiriliyor.
  • \S ifadesi ile yazının ilgili yerinde boşluk karakterleri (tab, space) dışında herhangi bir karakter olabileceği bildiriliyor.
  • \s ifadesi ile ilgili yerde sadece boşluk karakterlerinden birinin olabileceği bildiriliyor.

Şu ana kadar gördüğümüz bilgiler ışığında ilk karakteri 5 ile başlayan ikinci karakteri herhangi bir sayı olan ve son karakteri de boşluk olmayan bir düzenli ifade aşağıdaki gibi gösterilebilir.

^5\d\S

Not: Düzenli ifadeyi sağlayacak yazı mutlaka 3 karakterli olmalıdır.

Yukarıdaki ifadenin tamamına filtre denilmektedir.

3. Belirtilen gruptaki karakterlerden bir ya da daha fazlasının olmasını. istiyorsak + işaretini kullanırız.

\w+

filtresi bir ya da daha fazla alfanümerik karakterin olabileceği anlamına gelmektedir.

+işareti yerine * işareti kullanırsak çarpıdan sonraki karakterlerin olup olmayacağı serberst bırakılır.

4. Birden fazla karakter grubundan bir ya da birkaçının ilgili yerde olabileceğini belirtmek istiyorsak mantıksal veya | operatörünü kullanırız.

m|n|s

düzenli ifadesi ile ilgili yerde sadece m,n yada s karakterinin bulunabileceğini bildirilir. Bu ifadeyi parantez içine alıp sonunda + işareti koyarsak bu karakterlerden bir ya da birkaçının bulunabileceğini belirtmiş oluruz:

(m|n|s)+

5. Sabit sayıda karakterin olmasını istiyorsak {adet} şeklinde belirtmeliyiz.

\d{3}-\d{5} 

düzenli ifadesi ile “214–89431” yazısı sağlanır.

6. ? karakteri, kullanıldığı yerde önüne geldiği karakter en fazla bir en sıfır defa olabileceği bildirilir.

\d{3} B?A

düzenli ifadesi ile “532A” yazısı ile sağlanır.

7. “.” işareti ile ilgili yerde ‘\n’ karakteri dışında herhangi bir karakter bulunabilir.

\d{3} .A

düzenli ifadesine “521sA”, “478BA” uyar.

8. \b ile bir kelimenin belirtilen karakter dizisi ile sonlanmasını gerektiği bildirilir.

\d{3} dır\b

düzenli ifadesine “542dır” ve “dar361dır” yazıları uyar.

9. \B ile bir kelimenin başında ya da sonunda olmaması gereken karakterler bildirilir.

10. Köşeli parantezler kullanarak bir karakter aralığı da belirtebiliriz. Örneğin ilgili yerde sadece büyük harf karakterlerinin olmasını istiyorsak [A-Z] şeklinde kullanmalıyız. Bu aralık ilk ve son karakterler olmayabilir [A-P] ifadesi ile A ile P arasındaki karakterler alınır. Bu ifadeler sayılar [0–9] gibi için de geçerlidir.

Düzenli İfadelerin Gruplanması

Bir düzenli ifadeye uyan alt karakter dizisini çeşitli gruplara ayırabiliriz. Örneğin bir email adresini düşünün. Geçerli bir e-mail adresi kullanici@berkansasmaz.com şeklinde olabilir. Bir yazı içinde email adreslerini bulduktan sonra bu e-mail adresindeki kullanıcı adını, domain ismini ve domain tipini elde edebiliriz. Düzenli ifadeleri gruplamak için parantezler kullanılır. Şimdi bir e-mail adresini doğrulayacak düzenli ifadeyi yazalım.

\b(\w+)@(\w+)\.(\w+)

Bu ifadede \b ile kullanıcı adının tek bir kelime olmasını sağlıyoruz. \w+ ile ilgili yerde sadece alfanümerik karakterlerin bir ya da birden fazla bulunabileceğini belirtiyoruz. \. ile de ilgili yerde ‘.’ karakterinin olması gerektiğini bildiriyoruz.

Şimdi bir yazı içindeki e-mail adreslerini elde edip bir gruba nasıl ulaşacağımızı inceleyelim. Match sınıfının Groups özelliği ile her bir gruba erişebiliriz. Match sınıfında bildirilen indeksleyici sayesinde her bir gruba Groups[1], Groups[2] şeklinde erişebiliriz.

--

--