C# Öğreniyoruz | Engin Demiroğ Fan Club

C# Temelleri

Referans Tipler

Class

Kapsülleme (Encapsulation)

Arayüz (Interface)

Kalıtım (Inheritance)

Interface ile Kalıtım Inheritance Arasındaki Fark

Interface ve Abstract

Yapıcı (Constructor)

Linq

C# || Kapsülleme (Encapsulation) – get ve set Anahtar Sözcükleri

Kapsülleme (Encapsulation)

Sınıf içerisinde erişilmesi istenmeyen özellikler olabilir. Bunlar için kapsülleme uygulayabiliriz. Bu özellikler private anahtar değişkeni ile tanımlanır. Erişimi sınırlandırılmış olan bu özelliklere erişmek için metotları kullanırız.

namespace Pastane
{
   class Personel
       {
            public string ad, soyad, kat, bolum;
            string kurum = Asude Pastanesi;   //private kabul edilir. (Erişim belirteci)  istenilirse private yazılabilir
            public void personelBilgi(){
                Console.WriteLine(kurum);
                Console.WriteLine("{0} {1} {2} {3}", kat, bolum, ad, soyad);
            }
       }

   class AnaProgram
       {
            string void Main(string[] args)
            {
                Personel kisi = new Personel();  
                kisi.ad = "Lale";
                kisi.soyad = "Aygün";
                kisi.kat = "1. Kat";
                kisi.bolum = "Kasa";
                kisi.personelBilgi();
                Console.ReadKey();
            }
       }
}

ÇIKTI

Asude Pastanesi
1. Kat Kasa Leyla Aygün

get ve set Anahtar Sözcükleri

private anahtar sözcüğü ile ait oldukları sınıf dışından erişim kapatılan özelliklere get ve set anahtar sözcükleri ile erişim sağlanabilir. Bu anahtar sözcükler özelliklerden değer okumak veya özelliklere değer atamak için kullanılırlar. get ya da set sözcüklerinin ikisi de kullanılmak zorunda değildir. Değer atamak istendiğinde set, değer okumak istendiğinde get kullanılabilir.

class Ucgen
    {
         private state int x, y, z, toplam;

         public int kenarX
         {
             get { return x;}
             set { x = value;}
         }

         public int kenarY
         {
             get { return y;}
             set { y = value;}
         }

         public int kenarZ
         {
             get { return z;}
             set { z = value;}
         }

         public int topla()
         {
             toplam = x + y + z;
             return toplam;
         }

    }  

class AnaProgram
    {
         string void Main(string[] args)
         {
             Ucgen ucgen = new Ucgen();  
             ucgen.kenarX = 3;
             ucgen.kenarY = 4;
             ucgen.kenarZ = 5;
             Console.WriteLine("{0} + {1} + {2} = {3}", ucgen.kenarX, ucgen.kenarY, ucgen.kenarZ, ucgen.topla());
             Console.ReadKey();
         }
    }

Kaynak

  • Örneklerle Algoritma ve C# Programlama – Erhan Arı

C# – Typecast (Bilinçli Dönüşüm – Explicit Type Conversion) kullanımı

Basit bir tasarruf uygulaması kodu üzerinden typecast (bilinçli dönüşüm) kullanımını inceleyeceğiz. Kodun detaylarında saatte 2 tl elektrik yakan bir ampul ile totalde %20 tasarruf sağlayan bir ampulün girilen kullanma süresi gibi detaylar ile birlikte faturaya yansıyacak fiyatlarını yazdıran bir kod yazmamız isteniyor.

Kodumuz

  • 16. satırda yapılan typecast (bilinçli dönüşüm) işlemi (float) 80 / 100 şeklinde olmalı.
  • 17. satırda Console.WriteLine() metodu ile ekrana yazdıracağımız şeyleri yazdırırken dikkat etmemiz gerekenler vardır. Eğer bir metin yazdırmak istiyorsak bunları parantezde tırnak işaretleri içerisinde yazarız. Eğer ki yazdırmak istediğimiz şeyler kodun içinde tanımlamış olduğumuz değişkenler ise, bunları parantezde direkt olarak değişkenin ismini yazmak suretiyle yazdırabiliriz fakat eğer hem metin hem de değişken yazdırmak istiyorsak bunları + (artı) işareti ile aynı anda yazdırabiliriz. C dilindeki printf fonksiyonunun parantez içindeki gibi virgül ile ayırmak söz konusu değildir.

Kodun Çıktısı

Hatalar

  • Bu hatalı kodda, 16. satırdaki bölme işlemi parantez içinde yapılmış ve önünde de typecast kullanılmak istenmiş fakat bölme işleminin sonucu 0.8 olduğu için işlem yapıldıktan sonra float türüne dönüştürmek bir şey ifade etmeyecektir. Çünkü işlemin sonucu olarak sadece virgülden/noktadan öncesini alacaktır ve 0.8’nın 0’ını alarak floata dönüştürdükten sonra 0.0 elde edilmiş olur.
Hatalı ilk kodumuzun çıktısı budur.
  • Bu hatalı kodun çıktısında görüldüğü gibi sonuç 0 olur çünkü 50 * 0.0 = 0
  • Buradaki hatalı kodda ise, 16. satırda float ifadesi paranteze alınmalıdır. Aksi taktirde bu ifade tanımlanamayacaktır.
Hatalı ikinci kodumuzun çıktısı da bu şekilde.

Type Conversion (Typecast and Methods)

Type Conversion (tür dönüşümü)’ın bir alt formu olan Typecast konusu anladığım kadarıyla; basit, temel ve bir o kadar da önemli bir konu. Kodlamaya başladığım ilk zamanlarda öğrenmiş olmama rağmen ve hatta şu kısa süreli blog serüvenimde bile bununla ilgili bir örnek yapmış olmama rağmen, yaptığım matematik işlemlerinde bu konuyla ilgili detaylar yüzünden sonuca ulaşamadığımı fark ettiğim bir konu olduğu için tür dönüşümleri için ayrı bir başlık açmak istedim.

Veri türeleri arası dönüşüm, bir değişkenin değişik türdeki hâlinin başka bir değişkene atanmasıdır. Typecast (type casting) de denir. İki formu vardır.

Typecast

  1. Bilinçsiz Dönüşüm (Implicit Type Conversion): Düşük kapasiteli bir değişken, sabit ya da değişken ve sabitlerden oluşan matematiksel ifadenin daha yüksek kapasiteli bir değişkene atanması. Özel bir kod gerekmez
  2. Bilinçli Dönüşüm (Explicit Type Conversion): Genellikle derleyicinin izin vermediği durumlarda kullanılır. Bilinçli tür dönüşümüyle küçük türün büyük türe dönüştürülmesi gereksizdir, bilinçsiz tür dönüşümü bu işi yapar. Dönüşümü yapılan değişkenin tuttuğu değer, dönüştürülecek türün kapasitesinden büyükse veri kaybı gerçekleşebilir.

Bilinçsiz Dönüşüm (Implicit Type Conversion)

Bilinçli Dönüşüm (Explicit Type Conversion)

object Türüyle İlgili Kurallar

object türündeki bir değişkene başka herhangi bir türdeki değişken ya da sabit (string dışında) + işaretiyle eklenemez.

object türündeki bir değişkene herhangi bir türdeki değişken ya da sabit atanabilir (bilinçsiz tür dönüşümü). object türündeki bir değişkeni başka bir türe çevirmek için tür dönüştürme işlemi kullanılır. Dönüştürülen türlerin uyumlu olması gerekir.

Aynı satırda çifte dönüşüm yapılamaz.

string Türüyle İlgili Dönüşümler

x.ToString(): x değişken ya da sabitini stringe çevirip tutar.

string türüyle ilgili bilinmesi gerekenler:

Methods (System.Convert sınıfı)

Tür dönüşümü için metotlar System isim uzayında Convert sınıfındadır. Hemen hemen her tür her türe dönüştürülebilir. Değişken türlerinin CTS (Common Type System) karşılıkları bilinmelidir.

~Aritmetik Dönüşüm Hiyerarşisi

Kaynaklar:

C# – Challenges | Longest Word

Merhaba, bugün bahsedeceğim örnek kod, konularına tam anlamıyla hakim olduğum bir kod değil fakat bazı araştırmalar ve okumalar yaparak belli bir yere kadar ilerleme kaydettim ve bu kodu yazabildim. Bir sıkıntımız daha var ki, bu kodu yazmayı ve araştırmalarımı 8 Aralık 2019 Pazar günü tamamlamama rağmen yazısını bugün yani 26 Ocak 2020 Pazar günü yazabiliyorum. Açıkçası kodun ayrıntılarını maalesef unutmuşum. Şimdi sizlere anladığım ve hatırladığım kadarını aktarmaya çalışacağım.

Challenge Türü: Easy

Detaylar

Kodumuz

  • 2. satırda System.Linq kütüphanesi kullanılmalı. Kullanılmazsa 13. satırdaki, dizi elemanlarının büyükten küçüğe doğru sıralanması için kullandığımız OrderByDescending kodu çalışmaz. LINQ sorgusu hafızadaki (veya bir veri kaynağındaki) veri kümesinden program içinde SQL gibi sorgulama yaparak, seçilen verilerden oluşan yeni bir liste elde etmemizi sağlar. Linq hakkında daha fazla detay için bakabilirdiniz -> http://yazilim.cevapsitesi.com/Makaleler/5/linq-language-integrated-query-dile-entegre-edilmis-sorgu-nedir
  • 9. satırda kullandığım ayrac dizisini, yani bölmek istediğim cümledeki sözcükleri birbirinden ayıracak olan sembolleri ve boşluklarları tek tek kendim yazmak durumundayım. Buna maalesef alternatif bir çözüm bulamadım. Sıkıntı şu ki, benim orada tüm karakterleri yazmam mümküd değil ve benim yazmadığım bir sembol ile karşılaşılırsa kelimeleri ayrıması mümkün olmayacak. Örneğin kod ekrandan merhaba%okuyucu şeklinde bir girdi alsın. Bu yadığım kodda % sembolü bir ayraç olarak ifade edilmediği için buradaki merhaba ve okuyucu kelimeleri seçilemeyecek. Bunun yerine merhaba%okuyucu yu tek bir kelime olarak alacak. Bu satırdaki kod hatalı değil fakat eksiktir.
  • 10. satırda bir önceki yazımda da bahsettiğim .Split ifadesi yer alıyor. .Split() ifadesinde parantez içine yazdığımız şeyler bizim ayraçlarımız/bölücülerimiz oluyor. Kodumuz parantez içinde ne varsa, istenileni o ifadeyi gördüğü zamanlarda bölerek bir dizi haline getiriyor.
  • 13. satırda OrderByDescerding kodu sayesinde dizimizin elemanlarını büyükten küçüğe sıralayabiliyoruz. (Bu arada büyükten küçüğe, uzundan kısaya için Descending; küçükten büyüğe, kısadan uzuna için Ascending kullanılır.)

Kodun Çıktısı

Hatalar

  • Hatalı kodda 10. satır, ana kodda 13. satırda olan First() ifadesinin neden yazıldığına tam olarak hakim değilim fakat ortadan kaldırdığımızda aşağıdaki gibi bir hata veriyor. Anladığım kadarıyla En büyükten en küçüğe(en uzundan en kısaya) doğru sıraladığımız sözcük dizimizde tek bir elemanı seçip döndürmemiz geremiyor, bu da en uzun sözcük olan dizinin ilk eleman olduğu için First() ifadesi kullanılıyor. (asaba Last() gibi (dizinin son elemanı) ifadeler de var mı, kullanılıyor mu?)
hatalı kodun çıktısı böyle

C# – .Split fonksiyonu kullanımı

string.Split yöntemi, veri tabanında belirli işaretlere göre tek bir alanda tutmak istediğimiz kayıtları, bir veya daha fazla sınırlayıcı temelinde bölerek bir alt dizeler dizisi oluşturur. Genellikle bir dizeyi sözcük sınırlarında ayırmanın en kolay yoluymuş. Ayrıca, diğer belirli karakter veya dizelerde dizeleri ayırmak için de kullanılıyormuş

Split metodunun temel çalışma şekli şöyledir. Bir dizgeyi (karakter dizisi) belirli bir ayraç ile parçalayıp, parçalardan bir dizi döndürür.

Kullanım 1: C# string nesneleri ile kullanılır.  Aşırı yüklenmiş bir metottur fakat en sık kullanım şekli ayırıcı olarak bir karakter(char) girilmesi ile yapılır.

Kullanım 1.1
Kullanım 1.2
Kullanım 1 Çıktısı

Kullanım 2: C# split metodunun aşırı yüklenmiş bir metot olduğunu yazmıştım. Karakterlerden oluşan bir dizi ile birden fazla karakter ile de parçalamak mümkündür.

Örnek: 2019/12/07 18:06 şeklindeki tarihi parçalara ayırma

Kullanım 2
Kullanım 2 Çıktısı

Hatalar

  • Saati 18.06 şeklinde yazdığım ve ayrac dizisine . (nokta) elemanını eklemediğim için kodu yazdıramadım. Hata verdi. Aslında kod yazmayı bitirip derlendiğinde bir hata görünüyor fakat çalıştırmak istediğimizde özel bir hata oluşuyor. Syntax hatası olmamamsı kodun doğru olduğunu göstermez. Bu özel hata ile ilgili daha fazla detay isterseniz şu linkteki yazıyı inceleyebilirsiniz: https://docs.microsoft.com/tr-tr/dotnet/api/system.indexoutofrangeexception?view=netframework-4.8

Bu yazıyı yazarken yararlandığım kaynaklar:

C# – Challenges | First Factorial

Challenge Türü: Easy

Detaylar

Kodumuz

Kodun Çıktısı

Hatalar

  • 10. satırda tanımladığımız result değişkeni for döngüsünün dışında tanımlanmalı (örn. 7. satırda) çünkü for döngüsünün amacı bu değeri her seferinde yeni elde ettiğimiz değeriyle değiştirerek i ile çarpmak. (Sistem, [result*i=result] 1*1=1 1*2=2 2*3=6 6*4=24 şeklinde işliyor)
  • Aynı zamanda bu tanımlanacak result değişkenine bir ilk değer ataması yapılmalı yoksa null değeri döndürür (bildiğim kadarıyla?) ve bu atanacak ilk değer 1 olmalı ki çarpım işlemi yapıldığında ilk olarak kendisini versin. Örneğin 0 değeri atanırsa bu result değişkeni sürekli 0 değeri olarak kalır çünkü 0 ile çarpılan her sayı yine 0 değerini verir.
  • 12. satırda kendi yaptığım bir hatayı koyuyorum. Hızlı bir şekilde yazarken i yerine 1 yazmıştım. Fakat for döngüsünün içerisinde i‘ yi tanımlamamızın sebebi onu döngüyü çevirmesi amacıyla kullanmak zaten. i yerine 1 yazdığımız zaman result değerini sürekli olarak 1 ile çarpmış ve hep aynı sonucu elde etmiş oluruz. Burada result değişkeninin değeri 1 olduğu için sonuç her zaman result*1=1 olur.
  • 15. satırda num değil de result değişkenini yazmalıyız çünkü biz FirstFactorial metodunda result değişkenini döndürüyoruz ve Main metoduna bu değişkeni yolluyoruz. num ise bizim parametremizdir. Yani Main metodundan aldığımız değişken.
  • 20. satırda, kullanıcıdan aldığımız değeri C# dilini kullanarak yazdığımız kodda ve Main metodundan aldığımız için string türünde alıyoruz ve bunu FirstFactorial metoduna yollarken, işlem yaptırmak için vs. integer türünde yolluyoruz. Bunun için de Convert.ToInt32 gibi bir dönüştürücü kullanmamız gerekir.

Wow – Code Worth Checking | Come Look Here

Coderbyte platformunda bu kodu yazarken faktöriyel kodlarını inceliyordum ve bir kullanıcının yazdığı şu koda denk geldim. Aslında inceleyince gayet basit bir mantıkla yazılmış ve daha kısa, pratik görünüyor. İncelemeye, öğrenmeye değer bir kod olduğunu düşünüyorum. Ufku genişletmek için farklı bir bakış açısı olur en azından.

C# – Challenges | First Reverse

Bugün “Challenges | … ” başlıklı bir seriye başlamayı düşünüyorum. Coderbyte isimli kodlama değerlendirme platformundaki meydan okumalar bu seride olacak. C# öğrendiğim için istenilen kodları C# dilinde yazacağım. Yeni diller öğrendikçe bu diller değişebilir. Bu serinin ilk uygulaması “First Reverse” isimli challenge. Bu cahllenge’ın temel mantığı dizi elemanlarını / ekrandan girilen değişkenleri ters sıra ile yazdırma. ASlında daha önceki yazımda olan bir kod fakat buna rağmen kodu yazaken zorluk çektim. Hala öğrenememişim…

Challenge Türü: Easy

Detaylar

Kodumuz

  • 9. satırda reverse değişkenine bir ilk atama yapmayı unuymamalıyız. ( Burada string a = String.Empty; , string a = Null; veya string a = “”; kullanabiliriz.
  • 11. satırda for föngüsünün içerisini mutlaka (int i = str.Length – 1; i >= 0; i–) şeklinde yazmalıyız. (int i = str.Length; i > 0; i–) veya (int i = 0; i <= str.Length -1; i++) şeklinde yazmamız yanlış olur.
  • 13. satırda a değikenine i yi ekleyip devam etmek değil de i nin dizi içerisinde ifade ettiği değişkenin değerini ekleyip atamayı unutmamalıyız. (yani a += i; yazmamalıyız.) Ayrıca a = str[i]; gibi bir hata da yapmamalıyız.

Kodun Çıktısı

Hatalar

  • 11. satırda döngüyü döndürmeye sağlayacak olan int i değişkenine 0 değerini verip artırarak ilerlemek istiyor olabiliriz fakat bunu yaparken int i değişkeni ile string reverse değişkenlerini kıyaslayamayız. Türleri eşit olmalı. for (int i = 0; i <= str.Length -1; i++) veya for ( int i = str.Length -1; i >= 0; i–) yazabiliriz.
  • 11. satırda veya mutlaka for ( int i = str.Length -1; i >= 0; i–) şeklinde yazmalıyız. for (int i = 0; i <= str.Length -1; i++) şeklinde yazdığımızda kodun çıktısı yukarıdaki gibi olur. İçindeki harfleri ters çevirip yeni bir dizi oluşturmasını istediğimiz diziyi ters çevirmez. Ayrıca for (int i = 0; i < str.Length; i++) şeklinde de yazmamalıyız. Bu şekilde de hata veriyor (idk why?).
  • 13. satırda for döngüsünün içinde i‘ nin değerini direkt olarak reverse isimi string değişkene ekleyemeyiz çünkü burada i değişkeni bizim değişkenimizin değerini etkilemek / değiştirmek için kullanılmıyor. i değişkeninin kullanılma amacı reverse isimli string değişkenini bir diziye dönüştürüp bu dizinin her bir elemanının sırasını temsil etmektir. bu sayede i. eleman şudur i+1. eleman şudur gibi dizinin elemanlarını tersten yazdırmamız mümkün olacaktır.

C# – Yapıcı (Constructor) ve Get-Set (Property) kullanımı

YAPICILAR

  • Yapıcılar bir sınıftan new anahtar sözcüğüyle yeni bir nesne oluşturulduğu anda çalışan metottur. 
  • Yapıcı metot sınıf ismiyle aynı olmak zorundadır.
  • Geriye değer döndürmez. Fakat değer döndürmeyen metotlarda kullanılan void  yapıcı metotlarda kullanılmaz.
  • Yapıcı metotlar parametre alabilirler, aşırı yüklü olabilirler.
  • Kaynak: https://www.yazilimkodlama.com/programlama/c-siniflar-yapici-metot-constructor-kullanimi/

GET – SET KULLANIMI

  • Bir Class içerisinde bulunan bazı alanlara her zaman ulaşmak gerekmez. Çünkü bir nesneyi sürekli ulaşılabilir hale getirmek bilinçsiz kullanım, veri kaybı ve güvenliği gibi sorunları ortaya çıkartır. Zaten nesneleri tanımlarken varsayılan değer olarak “Private” erişim belirleyicisi olarak tanımlanması da bu tip gerekçelerden kaynaklanır. “Public” erişim belirleyicisi ise tamamen açık hale getirir. İşte tam bu noktada nesnelerimizin erişimini yönetmek için “Property” kavramı devreye girer. “Property” yani Özellik metotları nesnelerimiz üzerinde kontrollü kullanım sağlar.
  • Bir sınıf içerisinden farklı bir sınıf içerisindeki nesneye ulaşmak istiyorsak özellik (get -set) metotlarını kullanmalıyız.
  • Özellik metotları Get ve Set anahtar kelimesinden oluşan iki kod bloğundan oluşurlar.
  • Get metodu veri okunduğu zaman, Set metodu ise veri yazıldığı zaman (yani değer ataması yapıldığı zaman) yürütülür.
  • Kaynak: http://alkanfatih.com/c-property-ozellik-kullanimi-get-ve-set-nedir-nasil-kullanilir/

PUBLİC VE STATİC

  • Public olarak tanımlanan öğeye kod bloğunun içinden ve dışından erişilebilir, yani her yerden ulaşılabilir denilebilir.
  • Statik tanımlaması yapıldığında ise bu sınıf, herhangi bir nesne oluşturmadan; sadece adıyla çağırılıp erişim sağlanabilir.

Kodumuz

  • ogrenci sınıfının içerisinde bulunan Ad, Yas, Burc, Renk, Sayi isimli metotlardaki get ve set metotlarının sırası fark etmez. Yani bu kodda önce get metodu sonra set metodu yazılmış ve set ile değeri aldıktan sonra get ile geri gönderiyoruz gibi değil de bu ikilinin, bir metod içinde birbirini tamamladığını düşünmeliyiz. (sanırım?) (farklı sıralarda yazıp denedim ve aynı çıktıyı aldığım için bu kanıya vardım)
  • Değişkenleri yapıcının içerisinde tanımlayamayız. Sınıfın içerisinde, yapıcının dışında tanımladığımız değişkenleri, yapıcının içerisinde korumaya alırız. Bu sayede değişkenlerin değerlerine kolayca ulaşılıp değişiklik yapılaması engellenmiş olur. (6, 7, 8, 9 ve 10. satırlarda değişken tanımlama yapılır) (15, 16, 17, 18 ve 19. satırlarda değişkenler yapıcı içerisine konularak korunur)
  • Yapıcıyı ve içerisine get-set’leri koyacağımız metotları public şekilde tanımlıyoruz. (13, 22, 27, 33, 39 ve 45. satırlar)
  • 58, 59, 60, 61 ve 62. satırlarda nesneismi.değişkeninadı şeklinde değil de nesneismi.değişkeninmetodununadı şeklinde bir çağrılma yapılmalı. Örneğin, ad bir değişkendir ve değeri Rumeysa dır. ad değişkenini alan ve geri gönderen (içerisinde değişkene ait, sadece get-set metotlarının bulunduğu) metodun ismi ise Ad dır. Bu nedenle biz ad değişkenini Main metodu içerisinde çağıracağımız zaman, ad değişkeninin bulunduğu ogrenci sınıfından oluşturulan ogrnesne isimli nesne nokta değişkenin metodunun adı şeklinde yazarız.

Kodun Çıktısı

Hatalar

  • Değişkenler yapıcı içerisinde tanımlanmamalıydı.
  • Yapıcı ve içerisine get-set’leri koyacağımız metotlar static değil, public şekilde tanımlanmalıydı.
  • Başka bir sınıf içerisindeki değişkeni, Main metodu içerisinde nesne üzerinden çağıracağımız zaman; nesne adından sonra direkt olarak değişkenin adı değil, değişkenin bulunduğu metodun adı yazılmalıdır. Bu elbette ki her durum için geçerli değildir. Burada metodun adını kullanmamızın sebebi, bu metodun içerisinde sadece bu değişkenle ilgili get ve set metotlarının kullanılması, yani bu değişkenin döndürülmesi ile ilgilidir.

C# – Alan ve çevre hesabı yapabilen metodları içeren sınıf yazma ve main altında bu sınıfın objelerini oluşturma

Normalde, alan ve çevre hesabı yapabilen metodları, main metodu ile aynı sınıf içine yazabilirdik fakat bu kodda başka bir sınıf altında yazılan kodun, main içerisinde objesini nasıl oluşturup alan ve çevre hesabını yaptırdığımızı göreceğiz. Aynı kemik kod üzerinde 2 farklı örnek bulunuyor. Bu kodlar birbiriyle bağımsızdır. Birisi dik üçgenler üzerine, diğeri dikdörtgen üzerine yazılmıştır.

Kodumuz

1-Dik Üçgen Sınıfı

2-Dikdörtgen Sınıfı

Kodun Çıktısı

1-Dik Üçgen Sınıfının Çıktısı

2-Dikdörtgen Sınıfının Çıktısı

2 haftadır ara ara düşünüp kavrayamadığım, yazamadığım bir koddu. Ağlayacağım.

20 Ekim 2019. * R