19 Şubat 2023 Pazar

C# Temelleri 3

 Merhaba direk bu sayfaya gelenler için belirteyim, bu yazı serinin üçüncü yazısı. Önceki bölümler için 

https://ujk-ujk.blogspot.com/2023/01/c-temelleri-1.html ve 

https://ujk-ujk.blogspot.com/2023/02/c-temelleri-2.html

sayfalarına bakınız.

En son öğrendiklerimizi kullanarak uygulamalar yapıyorduk, devam edelim.



C# ile Bir Hesaplama Programı (Konsol Uygulaması)

İki tane girilen sayı arasında temel dört işlemi yapan basit bir program hedefliyoruz. Önce aklımıza gelen değişkenler ve bir giriş mesajı ile başlayalım. 

        static void Main(string[] args)
        {
            double sayı1 = 0;
            double sayı2 = 0;
            double sonuç = 0;

            Console.WriteLine("------------------");
            Console.WriteLine("Hesaplama Programı");
            Console.WriteLine("------------------");

            Console.ReadKey();
        }

Sayıları ve yapılacak işlemi kullanıcının girmesini isteyerek devam edelim.

            Console.Write("İlk sayıyı giriniz : ");
            sayı1 = Convert.ToDouble(Console.ReadLine());

            Console.Write("İkinci sayıyı giriniz : ");
            sayı2 = Convert.ToDouble(Console.ReadLine());

            Console.WriteLine("İşlemi giriniz : ");
            Console.WriteLine("\t+ : Toplama");
            Console.WriteLine("\t- : Çıkarma");
            Console.WriteLine("\t* : Çarpma");
            Console.WriteLine("\t/ : Bölme");

            Console.ReadKey();

Stringlerin içinde eklediğimiz \t ifadesi konsolda kursörü o noktada bir tab ileri götürür. Görsel olarak daha iyi görünecektir. İşlemi henüz almadık. İşlemi direk bir switc bloğunda parametre olarak konsoldan okuyalım ve sonuca göre işlemleri yapalım. 

            Console.WriteLine("İşlemi giriniz : ");
            Console.WriteLine("\t+ : Toplama");
            Console.WriteLine("\t- : Çıkarma");
            Console.WriteLine("\t* : Çarpma");
            Console.WriteLine("\t/ : Bölme");
           
            switch (Console.ReadLine())
            {
                case "+":
                    sonuç = sayı1 + sayı2;
                    Console.WriteLine($"İşlem : {sayı1} + {sayı2} = " + sonuç);
                    break;
            }

Şimdilik sadece toplama işlemini yaptık. Dikkat ederseniz değişik bir notasyon daha var burada. Konsola yazılacak string'i belirtirken değişkenleri direk çift tırnakların arasında kullandık. Böyle kullanabilmek için başlangıç tırnak işaretinden hemen önce bir $ işareti eklemeliyiz ve değişken isimlerini de süslü parantezler içinde vermeliyiz. Bu şekilde değişken değerinin direk string içinde kullanılmasına string'lerde interpolasyon denir.  sonuç değişkeni de daha önce gördüğümüz şekilde sonradan yazıya toplama işlemiy ile ekledik, ikisi bir arada kodumuzda görünsün.

Diğer işlemleri de switch bloğuna ekleyelim.

                case "-":
                    sonuç = sayı1 - sayı2;
                    Console.WriteLine($"İşlem : {sayı1} - {sayı2} = " + sonuç);
                    break;
                case "*":
                    sonuç = sayı1 * sayı2;
                    Console.WriteLine($"İşlem : {sayı1} * {sayı2} = " + sonuç);
                    break;
                case "/":
                    sonuç = sayı1 / sayı2;
                    Console.WriteLine($"İşlem : {sayı1} / {sayı2} = " + sonuç);
                    break;
                default:
                    Console.WriteLine("Geçerli bir işlem girilmedi!");
                    break;

Artık çalıştırıp deneyebiliriz. 

Son olarak kullanıcıya programı tekrar kullanmak isteyip istemediğini soralım. Daha önce bu amaçla while döngüsü kullanmıştık. Bu sefer değişiklik yapalım ve do-while döngüsü kullanalım, hem de öğrenmiş oluruz. Kodumuzun iş yapan kısmının tamamını döngü içine alalım.

            do
            {
                Console.WriteLine("------------------");
                Console.WriteLine("Hesaplama Programı");
                Console.WriteLine("------------------");

...
                    default:
                        Console.WriteLine("Geçerli bir işlem girilmedi!");
                        break;
                }

                Console.WriteLine("Devam etmek istiyor musunuz? (E/H)");

            } while (Console.ReadLine().ToUpper() == "E");

while döngüsünde karşılaştırma işlemi başta yapılıyordu ve karşılaştırma false sonuç verirse bloğun içine hiç girilmiyordu. do-while döngüsünde ise karşılaştırma işi sonda yapılır. Koşula bakılmaksızın bloğun içindeki kod çalıştırılır ve blok bitince karşılaştırma yapılır. Sonuç true olursa tekrar başa dönülür. Hem böyle yapınca tekrarOyna gibi boolean bir değişken tanımı yapmadan kısaca işi bitirmiş de olduk. Konsoldan okunan değeri direk karşılaştırma içinde kullanıyoruz. Amacımıza uygun olarak döngüler arasında işimize geleni kullanırız. 



C# array Değişkenler (Diziler)

Şimdiye kadar gördüğümüz değişken tipleri içinde sadece bir değer barındıran basit değişken tipleri idi. array değişkenler (diziler) içerisinde birden fazla sayıda değer saklayabilirler. Aslında bu önce gördüklerimiz gibi direk olarak bir değişken tipi değil, daha önce gördüğümüz ve bundan sonra da göreceğimiz değişken tiplerinden birini kullanan birden çok veriyi içinde saklayabilen bir değişken. Basit bir örnekle başlayalım, diyelim araç markalarını bir yerde toplayacağız. Bunların her biri String bir değer olacaktır.  

        static void Main(string[] args)
        {
            String araç = "BMW";

            Console.ReadKey();
        }

Tek bir değeri bu şekil tanımlıyorduk. Birden fazla String değer içeren bir array ise şöyle tanımlanır. 

            String araç = "BMW";
            String[] araçlar = { "BMW", "Mustang", "Corvette" };

Gördüğümüz gibi String'lerden oluşan bir dizi (array) tanımlamak için String kelimesi yanında [ ] köşeli parantez açma ve kapama işaretlerini ekliyoruz. Değerleri girerken de süslü parantez içinde ve aralarında virgül koyarak giriyoruz. Şimdi bu değerleri yazdırmak isteyelim.

            String[] araçlar = { "BMW", "Mustang", "Corvette" };

            Console.WriteLine(araçlar);

Çalıştırırsak

Ne ka güzel, ne ka güzeeel, bize değerleri yazacağına tipini yazdı. array'lerin içindeki değerleri yazdırmak için biraz kodlamak gerekiyor. Dizinin hangi elemanının değerini görmek istediğimizi belirtmemiz gerekiyor. Bunu belirtmek için değişken adının arkasına yine köşeli parantez içinde sol baştan itibaren kaçıncı elemanın değerini istediğimizi belirtmemiz gerekiyor. 

            Console.WriteLine(araçlar[0]);

Dizilerde index sayımı sıfırdan başlar. Sol baştaki eleman sıfır index, sonraki 1 index diye gider. Diğer elemanlarını da gösterelim.

            Console.WriteLine(araçlar[0]);
            Console.WriteLine(araçlar[1]);
            Console.WriteLine(araçlar[2]);

Peki olmayan bir elemanı yazdırmak istersek ne olur?

            Console.WriteLine(araçlar[3]);

Bir hata vererek (exception) program durur.

IndexOutOfRangeException oluştu diyor. Açıklama olarak da verdiğiniz index array sınırları dışında bir değeri gösteriyor demiş. Demek ki array'ler ile çalışırken bu sınırların dışına taşmamak için kodumuz içinde dikkat etmeliyiz. 

array değerlerin eleman sayıları sabittir ve sonradan ilave yapılamaz. Ama array içindeki elemanların birinin değerini değiştirmemiz mümkün. Aynı yazdırırken yaptığımız gibi index numarası ile eleman değerlerine atama yapabiliriz. 

            String[] araçlar = { "BMW", "Mustang", "Corvette" };

            araçlar[0] = "Tesla";

            Console.WriteLine(araçlar[0]);
            Console.WriteLine(araçlar[1]);
            Console.WriteLine(araçlar[2]);

array'lerin Length özelliğini kullanarak içerdikleri eleman sayısını görebiliriz. Bu özelliği kullanarak bir döngü içinde eleman değerlerini yazdıralım. 

            for (int i = 0; i < araçlar.Length; i++)
            {
                Console.WriteLine(araçlar[i]);
            }

Köşeli parantez içinde direk sayı yerine tabi ki değişken de verebiliriz. araçlar.Length değeri örneğimiz için 3 olacaktır ve döngü 0, 1 ve 2 index'leri için çalışacaktır. 

Dedik ki array'ler sabit boyutta tanımlanır. Bir array'i önce tanımlayıp sonradan değerleri girmek istersek, tanımlama esnasında eleman sayısını belirtmemiz gerekir. 

            String[] araçlar = new String[3];
            araçlar[0] = "Tesla";
            araçlar[1] = "Mustang";
            araçlar[2] = "Corvette";

Tanımlamanın sol tarafı yine aynı ama sağ tarafta değerleri gireceğimize boş String değerlerden oluşan 3 elemanlı bir array üretiyoruz. Bu notasyonu unutmayın ileride çok defa hatırlamamız gerekecek. Bu şekilde tanımlarken örneğe göre düşünürsek 3'den çok daha fazla elemanlı bir array de tanımlayabilir ve sonrasında sadece 3 tanesine değer girebiliriz. Fazla olan elemanlar sadece boş String değerinde olacaktır ama orada olmaya devam edeceklerdir. Mesela,

            String[] araçlar = new String[5];
            araçlar[0] = "Tesla";
            araçlar[2] = "Mustang";
            araçlar[4] = "Corvette";

bu kodu çalıştırsak

Aradaki boş satırlar aslında dizide olan ama değer girmediğimiz için en başta verilen boş String değerine sahip olan elemanlar. İlerledikçe array değerleri çok örneklerle görmeye devam edeceğiz. 



C# foreach Döngüleri

foreach döngüleri bir array elemanları üzerinden iterasyon (yineleme) yapmak için basit kolay ama biraz kısıtlı bir yol. Ama çok kullanılır. Önceki uygulamamızda array elemanlarını nasıl konsola yazdıracağımızı for döngüsü ile görmüştük. foreach döngüsü yapısı İngilizce bilenler için sanki konuşurmuş gibi daha kolay anlaşılabilir bir yapı sunuyor. 

        static void Main(string[] args)
        {
            String[] araçlar = { "BMW", "Mustang", "Corvette" };

            foreach (String araç in araçlar)
            {
                Console.WriteLine(araç);
            }

            Console.ReadKey();
        }

araçlar içindeki her bir araç için konsola araç değerini yaz gibi açık bir ifade var burada. foreach döngüsü parametre parantezi içinde ilk önce araçlar array içindeki değerler String olduğu için araç adında bir String değişken tanımlıyoruz. Sonra in bağlacı ile araçlar array içindeki değerlerle iterasyon yapılacağını belirtiyoruz. foreach döngü bloğuna ilk girildiğinde araç değişkeni "BMW" değerine sahip olur ve bu değerle blok kodu çalıştıktan sonra ikinci turda "Mustang" değeri ile kod çalışır. Böylece array içindeki tüm elemanlar için sırayla kod bloğu yinelenir. Boyut vermemize gerek yok foreach döngüsü array'in her elemanı için ayrı ayrı kod bloğunu yineleyecektir. 

Kısıtlamasına gelince for döngüsü kullandığımızda baştan sona ya da sondan başa doğru iterasyon yapabiliriz ya da mesela 2 elemandan birini göstermek için sayma işlemini i += 2 diye girebiliriz. foreach döngüsü ise sadece baştan sona ve tüm elemanlar için çalışır. Hangisi işimize gelirse onu kullanacağız, eğer tüm elemanlar üzerinde sırayla işlem yapacaksak basit olan foreach bize yeterli olacaktır. 




C# Metod Tanımlamak

Metodları daha önce kullandık ama ne oldukları nasıl tanımlandıklarını açıklama zamanı geldi. Metodlar bir ismi olan ve bir iş yapan kod bloklarıdır. Daha önce örneklerde gördüğümüz Console.WriteLine() , Math.Sqrt() gibi önceden tanımlanmış metodları kullanmıştık. 

Bir güzel örnek de kodumuzu şimdiye kadar içine yazdığımız Main() metodu. 

namespace CSharpTemelleri
{
    class Program
    {
        static void Main(string[] args)
        {

Aslında burada deniyor ki, CSharpTemelleri projesi , Program sınıfı , Main metodu. Bir C# uygulaması yapıyorsak mutlaka bu Main metodunun tanımlamasını yapmalıyız. Çünkü programı çalıştırdığımızda derleyici bu Main metodu içindeki kodu çalıştırır. Bu metodun tanımlamasını biz yapmadık, Visual Studio programı yeni bir proje üretmek istediğimizde otomatik olarak bu kodları oraya yazıp bizi zahmetten kurtardı. Nasıl olsa mecburen bunları yazacaktık. 

Metodun içinde çalışacak çok kodlar yazdık ama hiç metod tanımlamadık şimdiye kadar. Programlarımız büyüdükçe benzer işleri defa defa yapmamız gerekebilir, ya da sadece düzenli kod yazmak adına belirli işleri yapan kod bloklarını ayrı ayrı göstererek hakimiyet ve berraklık hedefleyebiliriz. On binlerce satır program sadece bir Main() bloğu içine yazılırsa hangi işi nerede yaptığımızı bulmak değişiklikler yapmak bazen günler sürebilir. Zaman içinde metodları ve daha gelişmiş bir çok yapıları kullanmak zorunda kalacağız. 

En temel metod kullanma sebebi tekrar tekrar aynı işleri yapmamaktır. Örnek olarak kullanıcıya sıkıntı vermek için defalarca iyi ki doğdun mesajı vereceğiz. 

        static void Main(string[] args)
        {
            Console.WriteLine("İyi ki doğdun Ümit");
            Console.WriteLine("İyi ki doğdun Ümit");
            Console.WriteLine("İyi ki doğdun sevgili Ümit");
            Console.WriteLine("İyi ki doğdun Ümit");
            Console.WriteLine();

            Console.ReadKey();
        }

Bu mesajları mesela 5 kere vermek istiyorsak ya beş defa alt alta yazacağız ya bir döngü içine koyacağız. Döngü içi tamam ama ya programımızın 50-100 satır sonrasında da tekrar bunları yapmak gereği olursa yine kod tekrarı olacak. Bir de örnekte 5 satırlık iş yapıyoruz ya 25 satır kod tekrarlanacaksa? Bu kadar kalabalık ve tekrarlanan kodun bir kere bile Main() içinde kullanılması görünüm berraklığını bozacaktır. Bu çeşitli sebeplerden dolayı tekrarlanan ya da çok kalabalık olan kodları metod içine toplamak gerekir. 

Metod tanımlamamızı Main() metoduna bakarak yapalım. Tabi ki bizim yazacağımız da bir metod olduğu için Main() bloğu dışına ama Program sınıfının içine yazıyoruz. Main() metodu bloğunun bittiği yerin hemen ardından yapılana bakarak bir tanımla yaparsak. 

        static void Main(string[] args)
        {
            Console.WriteLine("İyi ki doğdun Ümit");
            Console.WriteLine("İyi ki doğdun Ümit");
            Console.WriteLine("İyi ki doğdun sevgili Ümit");
            Console.WriteLine("İyi ki doğdun Ümit");
            Console.WriteLine();

            Console.ReadKey();
        }
        static void iyikiDoğdunMesajı()
        {

        }

static , void ne anlama geliyor şimdilik üzerinde durmayalım aynısını biz de yazdık. Metod isminden sonra gelen parantez içindekiler de metoda gönderilecek parametrelerdir ve bizim metodumuz şimdilik bir parametre kullanmayacağı için parantez içini de boş bıraktık. Metodumuz her çağrıldığında o 5 satır kodu çalıştıracak. Bu arada metodu kullanma işlemine de metodun çağrılması denir. 

Şimdi metodumuzun içinde çalışacak kod satırlarını Main() bloğu içinden kesip iyikiDoğdunMesajı() metod bloğumuz içine yapıştıralım.

        static void iyikiDoğdunMesajı()
        {
            Console.WriteLine("İyi ki doğdun Ümit");
            Console.WriteLine("İyi ki doğdun Ümit");
            Console.WriteLine("İyi ki doğdun sevgili Ümit");
            Console.WriteLine("İyi ki doğdun Ümit");
            Console.WriteLine();
        }

Bu metodu Main() bloğu içinden çağırmak için daha önce kullandığımız metodlar gibi yaparız. 

        static void Main(string[] args)
        {
            iyikiDoğdunMesajı();
            iyikiDoğdunMesajı();
            iyikiDoğdunMesajı();

            Console.ReadKey();
        }

Dikkatimizi çeken sadece metod ismini yazdık ama daha önce kullandığımız örnek Math.Sqrt() şeklinde idi. Metodun adı Sqrt() ama metod sistemde Math sınıfının içinde tanımlanmış olduğu için başında sınıfını da belirtmek zorundayız. Burada tanımladığımız metodsa Main() metodu ile aynı sınıfın (class Program) içinde olduğu için sınıf adını da vermemiz gerekmiyor, metodlar ikisi de aynı sınıfın içinde. 

    class Program
    {
        static void Main(string[] args)
        {
            ....
        }
        static void iyikiDoğdunMesajı()
        {
            ....
        }
    }

Daha ayrıntılar var ama zamanı geldikçe hepsine bakacağız. Şimdilik bu kadarı yeter. 

Şimdi metod tanımımızı parametre kullanacak şekilde geliştirelim. Diyelim isim adında bir değişken ile mesajın verileceği kişiyi belirtelim. 

        static void Main(string[] args)
        {
            String isim = "Ümit";

            iyikiDoğdunMesajı(isim);

            Console.ReadKey();
        }

iyikiDoğdunMesajı(isim) yazarak metodu parametre ile çağırdık. Bu durumda metodumuzu parametre kullanacak hale getirmeliyiz. 

        static void iyikiDoğdunMesajı(String isim)
        {
            Console.WriteLine("İyi ki doğdun " + isim);
            Console.WriteLine("İyi ki doğdun " + isim);
            Console.WriteLine("İyi ki doğdun sevgili " + isim);
            Console.WriteLine("İyi ki doğdun " + isim);
            Console.WriteLine();
        }

Metod tanımında isim sonrası parantez içinde beklenen parametrelerin tanımlamasını aralarına virgül koyarak yaparız. Örneğimizde bir String değer beklediğimizi ve bunu metod kodu içinde isim adlı değişken olarak kullanacağımızı belirtiyoruz. Yukarıda Main() metodu içinde de isim diye bir değişken kullanmıştık ama bunlar ikisi aynı şeyler değil yani,

        static void iyikiDoğdunMesajı(String x)
        {
            Console.WriteLine("İyi ki doğdun " + x);
            Console.WriteLine("İyi ki doğdun " + x);
            Console.WriteLine("İyi ki doğdun sevgili " + x);
            Console.WriteLine("İyi ki doğdun " + x);
            Console.WriteLine();
        }

şeklinde de yazabilirdik sadece anlamlı olsun diye aynı isim verildi. Metod tanımı içinde tanımlanmış değişkenler sadece o metod içinde geçerlidir. Zaten bu yüzden isim değişkenini parametre olarak metodumuza gönderiyoruz. Yoksa metodumuz içinde direk olarak Main() bloğu içindeki bu isim değişkenini kullanırdık.  Başka metodlarda aynı isimde olan bambaşka işler yapan değişkenler olabilir. 

Tabi ki metodu çağırırken parametre olarak değişken girmek zorunda değiliz, sabit değerler de girebiliriz. Örneğin,

            iyikiDoğdunMesajı("Hasan");
            iyikiDoğdunMesajı("Dilek");
            iyikiDoğdunMesajı(isim);

gibi. İkinci bir parametre olarak metodumuza yaş bilgisi de gönderelim. 

        static void Main(string[] args)
        {
            String isim = "Ümit";
            int yaş = 56;

            iyikiDoğdunMesajı(isim, yaş);

            Console.ReadKey();
        }
        static void iyikiDoğdunMesajı(String isim, int yaş)
        {
            Console.WriteLine("İyi ki doğdun " + isim);
            Console.WriteLine("İyi ki doğdun " + isim);
            Console.WriteLine("İyi ki doğdun sevgili " + isim);
            Console.WriteLine("Artık " + yaş + " yaşına geldin");
            Console.WriteLine("İyi ki doğdun " + isim);
            Console.WriteLine();
        }

Parametreleri hem metodu çağırırken hem de metodu tanımlarken aralarına virgül koyarak ayırıyoruz. 

Metodlarımız ayrıca Visual Studio IDE programımızda sağ üst köşede Çözüm Gezgini bölümünde listede gösterilir. Programlarımız çok büyüdükçe yüzlerce satır arasında gezmektense Çözüm Gezgini üzerinden tıklayarak metod tanımlarına direk geçebiliriz. 




C# return Kelimesi

return deyimi ile bir metoddan geriye bir değer dönülür. Mesela çok kullandığımız Console.ReadLine() metodu çağrılınca geriye bir String değer döner ve bu değer enter tuşu basılmadan önce klavyeden girilen yazıdır. 

Örnek olarak verilen 2 sayının çarpımını yapan bir metod yazalım.

    class Program
    {
        static void Main(string[] args)
        {
           

            Console.ReadKey();
        }
        static void çarp(double x, double y)
        {
            double sonuç = x * y;
            return sonuç;
        }
    }

Dikkat edersek return kelimesi altında yanlış yaptığımızı belirten bir kırmızı çizgi belirir. Neresi yanloş görmek için kursörü üzerine götürdüğümüzde

Diyor ki çarp() metodu void döndürecek ama sen double bir değer döndürmeye çalışıyorsun. Metod tanımlamasındaki o void kelimesi aslında metodun hiç bir şey döndürmeyeceğini anlatır. Metod isminden önce void yazmışsak o metod kendi içindeki kodu çalıştırır ve geri döner. Eğer metodumuz bir değer geri dönecekse bu noktada metodun geri döndüreceği değerin veri tipini yazmalıyız. Doğrusu şöyle olacak. 

        static double çarp(double x, double y)
        {
            double sonuç = x * y;
            return sonuç;
        }

Artık hata yok. Şimdi kullanıcıya işlem yapılacak sayıları soran ve metodu kullanarak sonucu konsola yazan bir kısa kod yazalım.

        static void Main(string[] args)
        {
            Console.WriteLine("İlk sayıyı giriniz : ");
            double x = Convert.ToDouble(Console.ReadLine());

            Console.WriteLine("İkinci sayıyı giriniz : ");
            double y = Convert.ToDouble(Console.ReadLine());

            double sonuç = çarp(x, y);
            Console.WriteLine(sonuç);

            Console.ReadKey();
        }

sonuç değişkenine çarp() metodundan dönen değer konuluyor, aynen daha önce sistem metodlarını kullanırken gördüğümüz gibi. Bundan sonra biz de kendi metodlarımızı tanımlayıp kullanabiliriz. 

Metod tanımlamasında return kelimesini kullanırken değişken kullanmak zorunda değiliz tabi ki, direk işlemi yapıp return kelimesine sonucunu verebiliriz.

        static double çarp(double x, double y)
        {
            return x * y;
        }





C# Metodlarda Tekrar Tanımlama (method overloading)

Şimdiye kadar dikkatinizi çekti ya da çekmedi ama hazır metodları kullanırken yardım kısmında bazen birden fazla parametre olasılığından bahseder. 

Mesela daha önce gördüğümüz random.Next() metodunun parametrelerini yazarken bize yardımcı olan popup içinde 3 öğenin 3üncü öğesi yazıyor. Bu aslında parametre girerken 3 seçeneğimiz olduğunu gösterir. Diğer olasılıklara bakmak istediğimizde o yazının sağında ve solunda yer alan ok işaretlerine tıkladıkça diğer olasılıkları bize gösterir. Bunlara bakarsak aynı metodu 2 parametreyle kullanacağımız gibi tek parametreyle ya da hiç parametre girmeden kullanabileceğimizi görürüz. Kendi metodlarımızı da böyle değişik olasılıklarla kullanmak istiyorsak metod tanımlamasını tekrarlarız. Buna method overloading denir.

Örnek olarak az önce yazdığımız çarp() metodunun eğer 2 sayı verirsek 2 sayıyı çarpmasını , eğer 3 sayı verirsek 3ünü çarpmasını şöyle sağlarız. 

        static void Main(string[] args)
        {
            Console.WriteLine(çarp(11, 22));
            Console.WriteLine(çarp(11, 22, 33));

            Console.ReadKey();
        }
        static double çarp(double x, double y)
        {
            return x * y;
        }
        static double çarp(double x, double y, double z)
        {
            return x * y * z;
        }

Tanımlama olarak birebir aynı ama parametre sayısı olarak farklı şekilde metodu 2 defa tanımladık. Metod tanımları içinde aynı ya da benzer şeyleri yapmak zorunda da değiliz. İsimleri aynı olmasına rağmen bu metodlar parametre sayılarına göre tamamen bağımsız 2 metod gibi çalışır. Mesela,

        static double çarp(double x, double y)
        {
            return x * y;
        }
        static double çarp(double x, double y, double z)
        {
            Console.WriteLine("Ooo, 3 tane sayıyı çarpamıyorum ben");
            return 0.0;
        }

Tamamen farklı gibi derken dönen değerin veri tipi bile farklı olabilir.

        static double çarp(double x, double y)
        {
            return x * y;
        }
        static String çarp(double x, double y, double z)
        {
            return "Ooo, 3 tane sayıyı çarpamıyorum ben";
        }

2 parametre ile metod çağrılınca double bir değer dönerken, 3 parametre ile çağrılınca String bir değer dönüyor.






C# params Kelimesi

Bazen parametre sayısının olasılıklarında ucu bucağı olmayan ihtimaller karşımıza çıkabilir. Örnek olarak düşünmek gerekirse, diyelim bir restoranda kasada müşterinin toplam hesabını çıkarıyoruz. Kaç tane yediği ürün varsa o kadar sayıda parametresi olan metod tekrar tanımlamalarına ihtiyacımız olacak.

    class Program
    {
        static void Main(string[] args)
        {
            double hesap = topla(3.99, 5.75, 15);

            Console.WriteLine(hesap);

            Console.ReadKey();
        }
        static double topla(double a)
        {
            return a;
        }
        static double topla(double a, double b)
        {
            return a + b;
        }
        static double topla(double a, double b, double c)
        {
            return a + b + c;
        }
    }

Ya müşteri 4 ürün aldıysa ya da 5, 6, 16, 99? Bu kadar metod mu tanımlayacağız? Bu durumda params kelimesini kullanarak değerleri bir array olarak girmemizi sağlayabiliriz. 

    class Program
    {
        static void Main(string[] args)
        {
            double hesap = topla(3.99, 5.75, 15, 2.5, 8);

            Console.WriteLine(hesap);

            Console.ReadKey();
        }
        static double topla(params double[] fiyatlar)
        {
            double toplam = 0;

            foreach (double fiyat in fiyatlar)
            {
                toplam += fiyat;
            }
            return toplam;
        }
    }

Metod çağrılırken parametresinde kaç tane değer girilirse o sayıda değerden oluşan bir array olarak metod içine alınır. Daha sonra metod içinde bu array elemanlarını foreach döngüsü ile kullanarak toplamı hesaplarız. 





C# exception (İstisnai Durum) Oluşması

Daha önce kısaca bahsetmiştik bazen programımıza yanlış bilgiler (değerler) girdiğimizde hata oluşuyor ve program çakılıyor. Ama bize verilen mesajda error değil exception oluştuğu söyleniyor. Daha önce size exception yani istisnai durum denmesinin sebebi bu durum oluştuğunda kod ile çakılmayı engellemek mümkün olduğu için demiştim. Şimdi bir örnekle exception oluşmasını ve programı çakılmaktan nasıl kurtaracağımızı görelim. 

        static void Main(string[] args)
        {
            Console.WriteLine("İlk sayıyı giriniz :");
            double x = Convert.ToDouble(Console.ReadLine());

            Console.WriteLine("İkinci sayıyı giriniz :");
            double y = Convert.ToDouble(Console.ReadLine());

            double sonuç = x / y;
            Console.WriteLine("Sonuç : " + sonuç);

            Console.ReadKey();
        }

Diyelim değer girerken sayı olmayan bir şey girdik,

Enter tuşuna bastığımız anda program çalışması kesilecek ve önümüze bir exception mesajı çıkacaktır.

System.FormatException , senden double'a dönüştürülebilecek bir değer bekliyordum ama girdiğin değer double değil demek istiyor. Yukarıdaki kırmızı Stop butonuna tıklayarak program çalışmasını durdurmayı unutmayın. Programımızda çalışırken bir exception oluştuğunu gördüğümüzde ya da zaman geçtikçe tecrübelerimizle kodun burası işlenirken exception oluşabilir düşüncesine kapılırsak ilk yapmamız gereken kodun olası bölümünü bir try bloğu içine almamızdır. 

        static void Main(string[] args)
        {
            try
            {
                Console.WriteLine("İlk sayıyı giriniz :");
                double x = Convert.ToDouble(Console.ReadLine());

                Console.WriteLine("İkinci sayıyı giriniz :");
                double y = Convert.ToDouble(Console.ReadLine());

                double sonuç = x / y;
                Console.WriteLine("Sonuç : " + sonuç);
            }

            Console.ReadKey();
        }

try bloğu tek başına yeterli değildir , hemen arkasından istisnai durum oluştuğunda yapılacak işleri de belirteceğimiz bir catch bloğu tanımlamamız gerekiyor.

            try
            {
                Console.WriteLine("İlk sayıyı giriniz :");
                double x = Convert.ToDouble(Console.ReadLine());

                Console.WriteLine("İkinci sayıyı giriniz :");
                double y = Convert.ToDouble(Console.ReadLine());

                double sonuç = x / y;
                Console.WriteLine("Sonuç : " + sonuç);
            }
            catch
            {
                Console.WriteLine("Ops! Bir sorun oluştu.");
            }

Sorunun ne olduğunu da kullanıcıya bildirmek istersek,

            catch (Exception e)
            {
                Console.WriteLine("Ops! Bir sorun oluştu.");
                Console.WriteLine(e.Message);
            }

catch bloğuna parametre olarak Exception e verdiğimizde e.Message ile exception tarafından verilen mesajı konsola yazdırabiliriz. Bütün exception'ları değil sadece duruma özel bir istisnai durumu yakalamak istersek, bize System.FormatException gelmişti, bu durumda sadece bunu yakalamak için,

            catch (FormatException e)
            {
                Console.WriteLine("Lütfen sadece SAYI giriniz!");
            }

Şeklinde yazarak kullanıcının yaptığı hataya direk nokta atışı yapabiliriz. Şimdi herhalde kafanızda bir şeyler beliriyordur. Bazen programları kullanırken "Bir hata oluştu" deyip sizin de "Yahu nedir bu hata yazsana" dediğiniz durumlarda o programı yazan arkadaşımız bütün olasılıkları inceleyecek kadar kod yazmamış demektir. 

Bir örnek olarak sıfıra bölme exception durumunu da düşünebiliriz. Ancak C# double sayılar için sıfıra bölmelerde +sonsuz ve -sonsuz değerlerine ulaşır ve program çakılmaz. Aslında bu uluslararası matematik teamülleri bu şekil hesap yapar. Bu yüzden sıfıra bölme istisnai durumunu görmek için sayılarımızı tamsayıya çevirelim.

            try
            {
                Console.WriteLine("İlk sayıyı giriniz :");
                int x = Convert.ToInt32(Console.ReadLine());

                Console.WriteLine("İkinci sayıyı giriniz :");
                int y = Convert.ToInt32(Console.ReadLine());

                int sonuç = x / y;
                Console.WriteLine("Sonuç : " + sonuç);
            }
            catch (FormatException e)
            {
                Console.WriteLine("Lütfen sadece SAYI giriniz!");
            }catch (DivideByZeroException e)
            {
                Console.WriteLine("Canım benim sayılar sıfıra bölünmez!");
            }

Görüldüğü üzere istediğimiz sayıda catch bloğunu arka arkaya dizerek olası tüm istisnai durumları değerlendirebiliriz. Alt tarafı bir bölme yapacağız, ya öyle olursa , ya böyle olursa derken kodumuz uzadıkça uzadı. Bu işi yapmaya niyetliyseniz , ihtiyacı görecek çalışan kod 100 satırsa kullanıcının yapması olası yanlışları önlemek için yazacağınız ilaveler bazen asıl programın 10 katı olabilir. Bu yüzden "bir hata oluştu" deyip sizi programdan atan yazılımcı arkadaşlara küfür etmeden önce bir daha düşünün derim.

Son olarak bir de finally bloğu ekleyebiliriz. finally bloğu içindeki kodlar bir exception oluşsa da oluşmasa da try-catch blokları sonrasında çalışır. Opsiyoneldir istersek kullanmayabiliriz. 

 ...
          catch (DivideByZeroException e)
            {
                Console.WriteLine("Canım benim sayılar sıfıra bölünmez!");
            }
            finally
            {
                Console.WriteLine("Hesaplama sona ermiştir.");
            }

Önce try bloğu işlenir eğer exception oluşursa try kodu çalışması orada kesilir ve ilgili (exception'ı yakalayan catch denir) catch bloğu çalışır ve sonrasında da finally bloğu içindeki kod her halükarda çalışır. finally blokları genelde bir dosya açıldığında ya da bir veri tabanı bağlantısı yapıldığında veri okuma işlemlerinin yapıldığı try blokları sonunda yapılan bağlantıyı kapatmak için kullanılır. 



C# Koşullu İşlem Operatörü ? (Ternary Operator)

Ternary operator (üçlü operatör denir) bir koşulun true ya da false sonucuna göre 2 değişik sonuç oluşturmak amacıyla kullanılır. Ternary operator soru işareti ile ifade edilir ve aslında bir if-else bloğunun kısaltılmış ifadesi gibidir. Kullanılışı şöyledir,

            (koşul) ? x : y;

Bu bir işlem olduğu için eşitliğin sağ tarafında yer alır. Eğer koşul true değerinde ise x değeri, koşul false değerinde ise y değeri sonuca verilir. Farkını görmek için aynı işi if-else bloğuyla  ve üçlü operatör ile yapalım. 

        static void Main(string[] args)
        {
            int sıcaklık = 20;
            String mesaj;
            if (sıcaklık >= 15)
            {
                mesaj = "Hava sıcak.";
            }
            else
            {
                mesaj = "Hava soğuk.";
            }
            Console.WriteLine(mesaj);

            Console.ReadKey();
        }

Üçlü operatör kullanarak kodumuzu çok daha kısa yazabiliriz.

        static void Main(string[] args)
        {
            int sıcaklık = 20;
            String mesaj = (sıcaklık >= 15) ? "Hava sıcak." : "Hava soğuk.";
           
            Console.WriteLine(mesaj);

            Console.ReadKey();
        }

sıcaklık >= 15 karşılaştırması true sonuç verirse "Hava sıcak." değeri false sonuç verirse "Hava soğuk." değeri mesaj değişkenine atanacaktır.

Bu basit operatör bazen çok kullanışlı olabilir, aklınızın bir kenarında kalsın. Temeli soru işareti, yani bir koşul sorgulanıyor, olumlu olursa hangi değer , olumsuz olursa hangi değer, aralarında da iki nokta üstüste.




C# String İnterpolasyonları

String interpolasyonları bir String içinde değişken değerlerini eklemek için kullanılan yöntemlerdir. Daha önce birçok örneklerini yaptığımız gösterim biçimi şöyleydi,

        static void Main(string[] args)
        {
            String isim = "Ümit";
            String soyİsim = "Kayacık";
            int yaş = 56;

            Console.WriteLine("Merhaba " + isim + " " + soyİsim + ".");
            Console.WriteLine("Siz " + yaş + " yaşındasınız.");

            Console.ReadKey();
        }

ve sonuç

Fakat interpolasyon kullanarak hem görsel olarak daha net anlaşılır hem de daha kolay ifade etme yolu vardır. String yazılırken çift tırnak açmadan hemen önce bir $ işareti koyarız ve String içinde kullanacağımız değişken isimlerini de süslü parantez içinde yazarız. Yukarıdaki aynı örnek ,

            Console.WriteLine($"Merhaba {isim} {soyİsim}.");
            Console.WriteLine($"Siz {yaş} yaşındasınız.");

Gördüğümüz gibi çok daha anlaşılabilir ve kolay. Tek yapmamız gereken tırnak açmadan önce bir $ işareti koymak. Editörümüz de String içinde kullanılan değişken isimlerini ayrı renklendirerek bize yardımcı oluyor. 

Bu yöntemi kullanırken değişken isimleri ile beraber kullanabileceğimiz opsiyonlar da var. Mesela değişken isminden sonra bir virgül koyup yazılacak değer için kaç karakter bir alan kullanılacağını belirtebiliriz. 

            Console.WriteLine($"Merhaba {isim, 3} {soyİsim}.");
            Console.WriteLine($"Siz {yaş, 10} yaşındasınız.");

ve sonuca bakalım

Gördüğümüz üzere 4 karakter olan isim değeri 3 e düşürülmüyor, ama 2 karakter olan yaş değeri 10 karakter içinde yazılıyor, geri kalan karakterler boşluk olarak ifade ediliyor. 

Eğer sayı değeri eksi olarak girersek değerimizi boşlukların soluna yaslanmış olarak gösterir.

            Console.WriteLine($"Merhaba {isim} {soyİsim}.");
            Console.WriteLine($"Siz {yaş, -10} yaşındasınız.");

ve sonuç





C# Çok Boyutlu array'ler

Konular biraz ağırlaşmaya başladı. Ama bunlar da temel konular, öğrenmemiz gerekiyor. Bir örnek amacıyla değişik araç üreticileri ve model isimlerini düşünelim.

        static void Main(string[] args)
        {
            String[] ford = { "Mustang", "F-150", "Explorer" };
            String[] chevy = { "Corvette", "Camaro", "Silverado" };
            String[] toyota = { "Corolla", "Camry", "Rav4" };

            Console.ReadKey();
        }

Şimdi bu araçları bir otoparkta markaların her biri bir sıra olmak üzere yerleştirdiğimizi düşünelim. Bunu ifade etmek için 2 boyutlu bir array kullanırız.

            String[,] parkYerleri = {
                { "Mustang", "F-150", "Explorer" },
                { "Corvette", "Camaro", "Silverado" },
                { "Corolla", "Camry", "Rav4" }
            };

İfadeyi yazarken düzenli görünsün diye hepsini tek satırda yazacağımıza sıra sıra yazdık. Nasıl olsa derleyici noktalı virgül işaretine gelene kadar hepsini tek bir satır olarak değerlendirecektir. 2 boyutlu array elemanlarının her biri yine array olan bir array'dir. Ama tam da öyle değildir, görsel olarak benzer, birazdan göreceğiz. 

2 boyutlu array elemanlarına erişmek için yine tanımlamada olduğu gibi virgüllü şekilde ifade ederiz. Mesela,

            Console.WriteLine(parkYerleri[0, 2]);

Komutu konsola sıfırıncı elemanın ikinci elemanını verecektir, yani Explorer. Aynı notasyonla değerin birini değiştirebiliriz.

            parkYerleri[0, 2] = "Fusion";

foreach döngüsünü 2 boyutlu array üzerinde kullandığımızda ise sanki tek boyutlu bir array'miş gibi sırayla her elemanı tek tek işler.

            foreach (String araç in parkYerleri)
            {
                Console.WriteLine(araç);
            }

ve sonuç

Araçları bir tablo şeklinde sıra sıra göstermek istersek iç içe for döngüleri ile yapabiliriz. 

            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    Console.Write(parkYerleri[i, j] + " ");
                }
                Console.WriteLine();
            }

Sonuçta şunu elde ederiz,

Daha yakışıklı gösterim için String interpolasyonu kullanabiliriz.

            for (int i = 0; i < 3; i++)
            {
                for (int j = 0; j < 3; j++)
                {
                    Console.Write($"{parkYerleri[i, j], -10}");
                }
                Console.WriteLine();
            }

ve sonuç,

yaşasın! 

Döngü için satır ve sütun sayılarını 2 boyutlu array değişkeninden de alabiliriz. Yani,

            for (int i = 0; i < parkYerleri.GetLength(0); i++)
            {
                for (int j = 0; j < parkYerleri.GetLength(1); j++)

şeklinde. Eğer parkYerleri değişkeni Length özelliğini kullanırsak bize toplam eleman sayısı olan 9 'u verecektir. GetLength() metodu ise bize parametresinde belirttiğimiz boyutun eleman uzunluğunu verir. Örnekte 3 e 3 bir tanımlama yaptık aslında biraz ters oldu. Mesela değerleri şöyle girseydik,

            String[,] parkYerleri = {
                { "Mustang", "F-150" },
                { "Corvette", "Camaro" },
                { "Corolla", "Camry" }
            };

Bu durumda parkYerleri.GetLength(0) bize 3 değeri dönecekti ve parkYerleri.GetLength(1) bize 2 değeri dönecekti. Boyutların sıralaması aynı elemanlara ulaşırken kullandığımız gibi parkYerleri[i, j] derken sıfırıncı boyut için i birinci boyut için j değeri veriyoruz. 

Şimdi başa dönelim, 2 boyutlu array'lerin görünümde elemanları array olan bir array gibi olduğunu ama aslında öyle olmadığını belirtmiştim. Elemanları array olan bir array, tam anlamıyla bir iç içe array'dir. Bir düşünelim Elemanları String olan bir array'i nasıl ifade ediyoruz?

            String[] ford = { "Mustang", "F-150", "Explorer" };

Bu durumda elemanları String array olan bir array nasıl ifade edilir?

            String[][] parklar;

Yaa, ya! Sevgili derleyicimizin bu notasyona bir itirazı yok. Peki değerleri nasıl vereceğiz?

            String[][] parklar = { ford, chevy, toyota };

Ya da değerleri direk girmek istersek,

            String[][] parklar = {
                new String[] { "Mustang", "F-150", "Explorer" },
                new String[] { "Corvette", "Camaro", "Silverado" },
                new String[] { "Corolla", "Camry", "Rav4" }
            };

Ayrıca iç içe array olunca satırlarda aynı sayıda eleman olmak zorunda da değil, mesela

            String[][] parklar = {
                new String[] { "Mustang", "F-150", "Explorer", "Fusion" },
                new String[] { "Corvette", "Camaro", "Silverado" },
                new String[] { "Corolla", "Camry", "Rav4" }
            };

olabilir. Tabi ki iç içe array elemanların değerlerine ulaşmak da tanımlandığı şekilde oluyor. 

            for (int i = 0; i < parklar.Length; i++)
            {
                for (int j = 0; j < parklar[i].Length; j++)
                {
                    Console.Write($"{parklar[i][j], -10}");
                }
                Console.WriteLine();
            }

ve sonuç

foreach döngüsü yaparsak da her bir array için çalıştıracağımız iç içe bir döngü yapmalıyız. 

            foreach (String[] sıra in parklar)
            {
                foreach (String araç in sıra)
                {
                    Console.Write($"{araç, -10}");
                }
                Console.WriteLine();
            }

İleride karşınıza çıkarsa ben gibi "bunlar aynı şeyler demeyin" 2 boyutlu array ve iç içe array arasında anlayış farklı, felsefe farklı, kullanım farklı vs. 

Bu noktadan sonra daha gelişmiş konulara gireceğimiz için bu kısmı burada yayınlayalım, sonraki bölümde görüşmek üzere kalın sağlıcakla..

Sonraki bölüm https://ujk-ujk.blogspot.com/2023/02/c-temelleri-4.html





Hiç yorum yok:

Yorum Gönder