C# Metotlar ve Fonksiyonlar 2

     Merhaba, yine uzun bir aradan sonra C# derslerine devam ediyorum. Aslında kafamda olan her 3 – 5 günde bir yeni konu yazmak; ancak işten güçten vakit bulamıyorum maalesef. Hızlı hızlı baştan savma da yazmak istemiyorum. O yüzden iki konu arasındaki mesafe bazen fazla olabiliyor. Neyse hemen yeni konuya geçelim.

DERS 9 : Metotlar ve Fonksiyonlar 2

     Önceki makalemizde metot nedir, nasıl yazılır, nasıl çağrılır, out ve ref anahtar kelimelerini anlattık. Bu makalemizde ise metotların aşırı yüklenmesinden ve öz yinelemeli yani recursive metotlardan bahsedeceğim. Ben makale boyunca öz yinelemeli yerine recursive kullanacağım. Hem yazması daha kolay hem de bir çok yerde karşınıza bu şekilde çıkar.

Metotların aşırı yüklenmesi (overload)

     Metotların aşırı yüklenmesinden (overload) kasıt, bir metot isminin birden fazla metoda verilmesidir aslında. Örneğin, “Topla” isminde bir metodunuz var ve bu metot parametre olarak iki tane tam sayı alıp, sonuç olarak bu iki tam sayının toplamını dönüyor. Peki aynı işi yapan ama parametre olarak üç tane tam sayı alan metoda da aynı ismi veremez miyiz? Evet, verebiliriz. Genelde isimleri aynı olan metotlar aynı işleri yaparlar; ancak tabiki de farklı işer de yaptırabilirsiniz. Aşağıdaki kodda metotların aşırı yüklenmesine örnek vermeye çalıştım.

using System;

namespace ders9
{
    class Program
    {
        static void Main(string[] args)
        {
            int first = Add(1, 3);
            int second = Add(1, 2, 3);
            string third = Add("One", "Three");
            float fourth = Add(1f, 3f);

            Console.ReadKey();
        }

        static int Add(int first, int second)
        {
            Console.WriteLine("int add");
            return first + second;
        }

        static int Add(int first, int second, int third)
        {
            Console.WriteLine("int triple add");
            return first + second + third;
        }

        static string Add(string first, string second)
        {
            Console.WriteLine("string add");
            return first + second;
        }

        static float Add(float first, float second)
        {
            Console.WriteLine("float add");
            return first + second;
        }
    }
}

     Yukarıdaki kodda da gördüğünüz gibi 4 tane “Add” metodu var. Program akışında da bu 4 metodu peşpeşe çağırdık. Peki derleyici, bu 4 metottan hangisini çağırmak istediğimizi nerden anladı? Metotları birbirinden ayırabilmek için metot imzası (signature) şeklinde bir terim kullanılır. Bu metot imzası; metodun adı, aldığı parametre sayısı ve parametrelerin türüne göre belirlenir. Metodun dönüş tipi bu imzaya dahil değildir. Kod içerisinde yazmış olduğumuz 4 Add metoduna iyica bakın. Hepsinin imzasının farklı olduğunu görebildiniz mi? Dördünün de isimleri aynı, burdan yola çıkamayız. Hemen parametre sayılarına bakalım. İkinci metodun parametre sayısı 3, diğerlerinin 2. Demek ki bir metodu bu şekilde ayırabildik. Gelelim diğerlerine, bunların parametre sayıları da aynı. O zaman son seçenek olan parametre tiplerine bakalım. Birisi int, int diğeri string, string sonuncusu da float, float tipinde parametreler alıyor. Yani hepsi birbirinden farklı. Demek ki bu durumda hiçbir problem oluşmayacak.

     Bu dört metodun altına bir de şu metodu ekleyelim:

static int Add(float first, float second)
{
    Console.WriteLine("float add return int");
    return Convert.ToInt32(first + second);
}

     Bu metodu eklediğimizde metodu yanlış yazdığımıza dair hiçbir belirti çıkmadı; ancak ilk yazmış olduğumuz kodun 4. satırının altı kırmızı ile çizildi. Mouse ile üzerine geldiğimizde de aşağıdaki hatayı görüyoruz:

The call is ambiguous between the following methods or properties: ‘ders9.Program.Add(float,float)’ and ‘ders9.Program.Add(float,float)’

     Peki bu ne demek? Visual Studio bize diyor ki, sen burda Add metodunu çağırmaya çalışıp buna iki tane float parametre gönderiyorsun ama bu şekilde yazılmış iki metot var, ben hangisini çağırayım? Aslında baktığınızda metotlar birebir aynı değil, çünkü birinin dönüş değeri int diğerinin float. Bu da demek oluyor ki, metotlar karşılaştırılırken dönüş değerleri hesaba katılmıyor. Bunun da sebebi (bence), metodun dönüş değerini bir değişkene atamak zorunda olmamamız veya bilinçli tip dönüşümü kullanabilir olmamız. Dolayısıyla hangi dönüş değerine sahip metodu çağırdığımız her zaman bilinemeyebilir.

     Metotların aşırı yüklenmesi bu şekilde. Aynı isme sahip metoda farklı tipte veya farklı sayıda parametreler geçerek, metotları aşırı yükleyebilirsiniz.

Recursive (öz yinelemeli) metotlar

     Herhangi bir programlama dilinde, sizi en çok zorlayacak konulardan birisidir recursive metotlar. Meslek sahibi olan birçok yazılımcının bile hâlâ recurisve metot yazamadığına eminim; ancak gözünüzü hemen korkutmayayım. Mantığını kavradıktan sonra çok da zor bir konu değil. Zorluğunun yanı sıra, recursive metotları ne sıklıkla kullanıp kullanmayacağınız ayrı bir mesele. Bu başlık altında ona da değinmeye çalışacağım.

     Recursive metot nedir? En basit ve kısa tabiriyle, kendi kendini çağıran metotlara recursive metot denir. Belki bazılarınızda hemen bir ışık yandı, “Nasıl yani? Kendi kendini çağırıyorsa bu sonsuza kadar gitmez mi?” şeklinde. İşte ilk anahtar cümlemiz bu: “Bu metot sonsuza kadar çalışmamalı!”. Recursive metot yazarken 4 anahtar adım vardır:

  1. Koşulu kontrol et
  2. İşlemleri yap
  3. Kendini çağır
  4. Sonucu dön

     Recursive metot yazarken daima bu dört kuralı uygulamalısınız. İkinci ve üçüncü adımın yeri değişebilir; ancak birinci ve dördüncü adımın (istisnalar dışında) değişmemesini tavsiye ederim. Hatırlarsanız geçen yazımızda faktöriyel hesaplayan bir metot yazmıştık. Metot içerisinde bir for döngüsü vardı ve parametre ile verilen sayıdan sıfıra kadar olan sayıları çarpa çarpa gidiyordu. Şimdi bu metodu recursive yazalım.

using System;

namespace ders9a
{
    class Program
    {
        static void Main(string[] args)
        {
            int result = CalculateFactorial(5);
            Console.WriteLine("Result: " + result.ToString());
            Console.ReadKey();
        }

        static int CalculateFactorial(int value)
        {
            if (value == 0)
            {
                return 1;
            }

            int result = value * CalculateFactorial(value - 1);
            return result;
        }
    }
}

     Adım adım inceleyelim. Metodumuzun ismi yine CalculateFactorial. Parametre olarak bir tam sayı alıyor, biz bu sayının faktöriyelini hesaplatacağız. Dönüş değeri olarak da yine tam sayı dönüyoruz. Şimdi, yukarıdaki 4 anahtardan yola çıkmamız gerekirse, ilk adım: “koşulu kontrol et”. Koşulu kontrol et ne demek?

     Yazdığımız metot sonsuza kadar çalışmayacağı için bir yerde durması gerekiyor. Bu durma koşulu neyse onu kontrol et ve o koşul sağlandığında artık kendini çağırma. Bizim metodumuzda bu koşul, parametrenin 0 olması durumu. 0’ın ve 0’dan küçük sayıların faktöriyeli olmayacağı için, eğer parametre 0 gelirse, sonuç değeri olarak tekrar kendini çağırma ve sadece 1 değerini dön.

     İlk adım tamam, ikinci adım işlemleri yap. Bu kısım her seviyede yapacağınız işlemleri yazacağınız kısım. Faktöriyel için örnek vermek gerekirse: 5 faktöriyeli nasıl hesaplarsınız? 5 * 4! olarak hesaplayabilirsiniz değil mi? Sonra 4 faktöriyeli de aynı mantıkla 4 * 3! olarak hesaplayabilirsiniz. Ta ki en son 1 * 0! olana kadar. Bu mantıktan yola çıkarak bizim burda yaptıracağımız işlem n * (n-1)! (yani çarpma işlemi) olmalı. n değeri metota geçilen parametre olduğu için: n * CalculateFactorial(n-1) şeklindeki yazım, yukarıda anlattığım hesaplama yöntemini gerçekleştiriyor olacak. Bu arada, çarpma işlemi yaparken işlemleri yap adımı ile beraber kendini çağır adımını da gerçekleştirmiş olduk.

     Son adımda, yaptığımız işlemlerden elde ettiğimiz sonucu metodu çağıran yere geri döndük.

     Kod içerisinde her işlemde ekrana yazı yazdırarak belki biraz daha iyi anlatabilirim diye düşündüm ve metoda aşağıdaki gibi eklemeler yaptım. 5! için kodu çalıştırdığımda aşağıdaki ekran görüntüsüne ulaştım.

using System;

namespace ders9a
{
    class Program
    {
        static void Main(string[] args)
        {
            int result = CalculateFactorial(5);
            Console.WriteLine("Result: " + result.ToString());
            Console.ReadKey();
        }

        static int CalculateFactorial(int value)
        {
            Console.WriteLine("value 0 mı kontrol et. value: " + value.ToString());
            if (value == 0)
            {
                Console.WriteLine("value = 0. O zaman 1 değerini dön.");
                return 1;
            }

            Console.WriteLine("çarpım değerini hesapla. value: " + value.ToString());
            int result = value * CalculateFactorial(value - 1);

            Console.WriteLine("sonucu geri dön. sonuç: " + result.ToString());
            return result;
        }
    }
}

recursive

     Recursive metotlar da bu şekilde. Recursive’e başlarken, kullanıp kullanmama ile alakalı bir şeyler yazacağımı söylemiştim. Sizin de gördüğünüz gibi basit bir for döngüsüyle yapılabilecek işlemi neden recursive metot olarak yazalım? Eğer işleminiz gerçekten bir for veya while döngüsü ile yapılabilecek kadar basitse zaten recursive metotlara hiç bulaşmayın. Hem yazarken çok zorlanırsınız hem de performansı öldürürsünüz. Recursive metotlar döngülere göre çok fazla performans kaybı yaşatırlar. Ayrıca her metot çağrımı için bellekte yer ayrılacağından çağrım sayısı sınırlıdır. Örneğin ben en fazla 65 içe içe çağrımı görebildim. 66 olduğunda değer hep 0 oldu (stack overflow hatası aldığından dolayı).

     Yine çok uzun bir makale oldu. Umarım birilerine faydalı olabilmişimdir. Bir sonraki makalemde sınıflara başlıyorum. Artık önemli konular başlıyor. Buraya kadar bir nevi ısınma turuydu. Bundan sonra işler daha da güzelleşecek. Hepinize iyi günler dilerim, bir sonraki makalemde görüşmek üzere…

Gelen arama terimleri:
  • c# kendini çağıran metot (3)

Bu makaleye 1 yorum yapılmış

  • Serkan

    19 Ocak 2014 at 12:07 Cevapla

    Bir metod yazarken en çok zorlandığım kısım ona isim vermek 🙂 Yeni doğan bir çocuğa isim veriyormuşum gibi hissediyorum 🙂

Yorum Yapın

*

warning
www.kemalkefeli.com.tr üzerindeki herhangi bir yazının veya kodun izinsiz olarak başka bir yerde kullanılması yasaktır.