Genellikle belirli bir sorunu çözmek için metotun yürütüldüğü çevreye bağlı olan ek bilgiler gerekebilir.
Eğer gövdesinde bir karenin alanını bulan bir metot varsa, o alanı (S = a2 denklemi) bulan bir algoritmanın da var olması gerekir. Alanı karenin kenar uzunluğuna bağlı olduğuna göre, her kare için bu denklemi hesaplamak için, metota karenin yan uzunluğuna ait bir değer geçirilmesi gerekecektir. Bir şekilde bu değeri geçirmek zorunda olmamızın nedeni budur ve bu amaç için parametreleri kullanıyoruz.
9.9.1 Parametreli Metot Bildirimi
Metotumuza gerekli bilgileri geçirmek için parametre listesini kullanıyoruz. Önceden de belirtildiği gibi, yöntem adını takip eden parantezler arasında bunu bildirmeliyiz:
static (
)
{
// Method's body
}
|
Parametre listesi
, metot mantığının uygulanması için kullanılacak olan ve virgül ile ayrılmış sıfır yada daha fazla değişkenin bildiriminden oluşan bir listedir.
= [1> 1>[, i> i>]],
where i = 2, 3, …
|
Bir metotu oluşturduğumuzda, belirli bir algoritma geliştirmemiz için belirli bilgiler gerekiyor, i> türündeki o değişkeni listeden seçiyoruz, ve böylece onu adıyla i> kullanıyoruz.
Listedeki parametreler herhangi bir türden olabilir. Temel türler olabilir (int, double, …), veya nesne türleri (örneğin string veya dizi – int[], double[], string[], …).
9.9.1.1 Bir Şirket Logosunu Görüntülemek İçin Metot – Örnek
Yukarıda belirtilenleri daha net yapmak için, "Microsoft" logosunu gösteren örneği değiştireceğiz:
static void PrintLogo(string logo)
{
Console.WriteLine(logo);
}
|
Şimdi, metotumuzu yürüttüğümüzde, diğer şirketlerin logosunu da görüntüleyebileceğiz, sadece "Microsoft" için değil. Bu mümkündür, çünkü şirket adını geçirmek için string türünde bir parametre kullandık. Örneğimiz, parametre listesinde verilen bilgilerin nasıl kullanılacağını göstermektedir – parametre listesinde tanımlı logo değişkeni tanımda verilen adı ile metotun gövdesinde kullanılmaktadır.
9.9.1.2 Kitap Fiyatlarının Toplamını Hesaplamak İçin Metot - Örnek
Yukarıda belirtmiştik ki, belirli bir metot için parametre olarak gerekli olduğunda dizileri kullanabiliriz, (int[], double[], string[], …). Şimdi bunu göstermek için başka bir örneğe bir göz atalım.
Bir kitapçıda olduğumuzu ve aldığımız tüm kitapları ödemek için gereken para miktarını hesaplamak istediğimizi düşünün. decimal[] türünde bir dizi olarak tüm kitaplar için fiyat alan ve sonra ödemek zorunda olduğumuz toplam tutarı döndüren bir metot oluşturacağız.
static void PrintTotalAmountForBooks(decimal[] prices)
{
decimal totalAmount = 0;
foreach (decimal singleBookPrice in prices)
{
totalAmount += singleBookPrice;
}
Console.WriteLine("Tüm kitapların toplam tutarı:" +
totalAmount);
}
|
9.9.1.3 Girdisine Göre Metot Davranışı
Parametreli bir metot bildirildiğinde, amacımız metotu her çağırdığımızda sonucun girdiye göre değişmesidir. Başka bir söyleyişle, algoritma aynıdır, ama girdinin değişmesi sonucu değiştirir.
|
Parametreli metotların davranışı parametrelerin değerlerine bağlıdır.
|
9.9.1.4 Bir Sayının Pozitif Olup Olmadığını Göstermek İçin Metot – Örnek
Metotların girdilerine bağlı olarak yürütüldüklerini açıklamak için şimdi başka bir örneğe göz atalım. Metot girdi olarak int türünde bir sayı alır, ve buna göre konsola "Pozitif", "Negatif" yada "Sıfır" döndürür.
static void PrintSign(int number)
{
if (number > 0)
{
Console.WriteLine("Pozitif");
}
else if (number < 0)
{
Console.WriteLine("Negatif");
}
else
{
Console.WriteLine("Sıfır");
}
}
|
Şimdiye kadar tek bir parametre içeren parametre listeleriyle metotlar için bazı örneklerimiz vardı. Ancak metotu bildirirken, ihtiyaç duyduğu kadar birden çok parametresi olabilir.
Eğer örneğin, iki değerin en büyüğünü almak istiyorsanız, bu metot iki parametre gerektirir:
static void PrintMax(float number1, float number2)
{
float max = number1;
if (number2 > max)
{
max = number2;
}
Console.WriteLine("En büyük sayı: " + max);
}
|
9.9.1.6 Çoklu Parametreli Metotların Bildirimindeki Fark
Birden çok parametre ile bir yöntem bildirildiğinde, parametreleri aynı türde olsa bile dikkat etmeliyiz ki, kısa yoldan değişken tanımlaması kullanımı yasaktır. Bu nedenle metot bildirimindeki aşağıdaki satır geçersizdir ve derleyici hata üretir:
Parametrelerin türü, bazıları aynı türde olursa olsun, her parametreden önce açıkça yazılmış olmalıdır.
Dolayısıyla aşağıdaki bildirim geçerli değildir:
static void PrintMax(float var1, var2)
|
Bunu yapmak için doğru yol şudur:
static void PrintMax(float var1, float var2)
|
9.9.2 Parametreli Metotları Çağırmak
Bir yada birkaç parametre ile metot çağırma, parametreleri olmadan çağırma gibi aynı şekilde yapılır. Fark, metot adını takip eden parantezler arasında değerleri vermemizdir. Bu değerler (argüman – bağımsız değişken denir) bildirim formundaki uygun parametrelere atanacaktır ve metot çalıştırıldığında kullanılacaktır.
Parametreli metotlar için bazı örnekler aşağıdaki gibidir:
PrintSign(-5);
PrintSign(balance);
PrintMax(100.0f, 200.0f);
|
9.9.2.1 Parametreler ve Argümanlar Arasındaki Fark
Devam etmeden önce, metot bildirimindeki parametre listesindeki adlandırmalar ile metot çağrılırken geçen değerleri ayırt etmeyi öğrenmeliyiz.
Daha netleştirmek için, bir metotu bildirdiğiniz zaman, parametre listesindeki elemanların hehangi birine parametre diyeceğiz (diğer kaynaklar formal parametre olarak adlandırabilir).
Bir metotu çağırdığınız zaman, parametrelere atamak için kullandığınız değerler argüman (bağımsız değişken) olarak adlandırılır.
Diğer bir deyişle, parametre listesindeki (var1 ve var2) elemanlarına parametre denir:
static void PrintMax(float var1, float var2)
|
Buna göre, yöntem çağırmasındaki (-23.5 ve 100) değerleri argüman – bağımsız değişkenler olarak adlandırılır:
PrintMax(100.0f, -23.5f);
|
9.9.2.2 Temel Türden Argümanların Geçirilmesi
Az önce açıkladığımız gibi, C#’da metot argümanı olarak bir değişkeni geçirdiğiniz zaman, değeri metot bildirimindeki parametreye kopyalanır. Bundan sonra metot gövdesinde, kopya değer kullanılacaktır.
Ancak farkında olmamız gereken bir şey var. Eğer bildirilen parametre temel bir tür ise, bağımsız değişkenlerin kullanımı argümanı değiştirmez, yani metotun çağrıldığı kod içinde argümanın değeri değişmeyecektir.
Öyleyse eğer aşağıdaki gibi kod parçası var ise:
static void PrintNumber(int numberParam)
{
// Modifying the primitive-type parameter
numberParam = 5;
Console.WriteLine("PrintNumber() metotunda, değişiklik " +
"sonrasında, numberParam değeri: {0}", numberParam);
}
|
Main() içinden metotun çağrılması:
static void Main()
{
int numberArg = 3;
// Copying the value 3 of the argument numberArg to the
// parameter numberParam
PrintNumber(numberArg);
Console.WriteLine("Main() metotunda numberArg değeri: " +
numberArg);
}
|
numberArg değeri 3, numberParam parametresine kopyalanır. PrintNumber() metotu çağrıldıktan sonra, numberParam için 5 değeri atanır. numberArg değişkeninin değeri bundan etkilenmez, çünkü bu metotun çağrılmasıyla numberParam değişkeni argüman değerinin bir kopyasını tutar. Bu nedenle PrintNumber() 5 sayısını yazdırır. Dolayısıyla, PrintNumber() metotunun Main() içinden çağrılması sonrasında yazdırılan numberArg değeridir ve görüldüğü gibi bu değer değişmez. Yukarıdaki satırın sonucu aşağıya yazdırılmıştır:
PrintNumber() metotunda, değişiklik sonrasında, numberParam değeri: 5
Main() metotunda numberArg değeri: 3
|
9.9.2.3 Referans Türü Argümanların Geçirilmesi
Referans türü (dizi) parametreleri olan bir metotu bildirmek (ve böylece çağırmak) için, çok dikkatli olmalıyız.
Bu dikkatin gerekli nedenini açıklamadan önce, kendimize "Diziler" Bölümü’nden bir şey hatırlatmamız gerekmektedir. Başka bir referans türü olarak bir dizi, değişken işaretçisi (nesne referansı) ve bir değerden oluşur – bilgisayarın belleğinde tutulan gerçek bilgiler (nesne diyoruz). Bu durum için nesne, gerçek eleman dizisidir. Ancak, bu nesnenin adresi değişken içinde tutulur (yani dizi elemanlarıın bellekte yerleştirildiği adres):
Bu nedenle, C# dizileri ile işlem yapacaksak, diziyi bildirmek için kullandığımız bu değişken tarafından (adres, işaretleyici, referans) onlara her zaman özellikle erişim sağlayabiliriz. Bu referans türü için bir başka ilkedir. Dolayısıyla, referans türü bir argüman bir metota geçirildiği zaman metotun parametresi referansın kendisini alır. Ancak nesne (gerçek dizi) ne olur? Ayrıca kopyalanır ya da hayır?
Bunu açıklamak için şu örneğe bakalım: Bir parametre olarak geçirilen bir dizinin ilk öğesini değiştiren ModifyArray() metotunu varsayalım. İlk öğenin değeri 5 olarak başlatılmıştır ve sonra dizinin öğelerini köşeli parantezlerle çevrili ve virgülle ayrılmış olarak yazdırır:
static void ModifyArray(int[] arrParam)
{
arrParam[0] = 5;
Console.Write("ModifyArray() içinde parametre: ");
PrintArray(arrParam);
}
static void PrintArray(int[] arrParam)
{
Console.Write("[");
int length = arrParam.Length;
if (length > 0)
{
Console.Write(arrParam[0].ToString());
for (int i = 1; i < length; i++)
{
Console.Write(", {0}", arrParam[i]);
}
}
Console.WriteLine("]");
}
|
Aynı zamanda, yeni oluşturulan ModifyArray() metotunu çağırdığımız bir Main() metotu bildirelim:
static void Main()
{
int[] arrArg = new int[] { 1, 2, 3 };
Console.Write("ModifyArray() öncesinde argüman: ");
PrintArray(arrArg);
// Modifying the array's argument
ModifyArray(arrArg);
Console.Write("ModifyArray() sonrasında argüman: ");
PrintArray(arrArg);
}
|
Kod yürütmesinin sonucu ne olacaktır? Bir göz atalım:
ModifyArray() öncesinde argüman: [1, 2, 3]
ModifyArray() içinde parametre: [5, 2, 3]
ModifyArray() sonrasında argüman: [5, 2, 3]
|
Açıktır ki, ModifyArray() metotunun yürütmesinden sonra, argArry değişkeninin gösterdiği dizi [1,2,3] değerlerinden değil, bunun yerine [5,2,3] değerlerinden oluşmaktadır. Bu ne anlama geliyor?
Böyle bir sonuç için sebep, referans türü argümanların geçirilmesiyle, sadece nesne adresini tutan değişken değeri kopyalanır. Unutmayın ki, nesnenin kendisi kopyalanmaz.
|
Referans türünden bir argüman geçirildiğinde, kopyalanan tek şey nesne referansını tutan değişkendir, nesne verileri değildir.
|
Şimdi açıklananın ne olduğunu göstermeyi deneyelim. Yukarıda kullandığımız örnek için birkaç çizimden faydalanacağız. ModifyArray() metotu çağrıldığında, arrParam parametresinin değeri tanımlı değildir ve herhangi bir nesne için bir başvuru tutmamaktadır (gerçek bir dizi değildir).
ModifyArray() metotunun çağrılması anında, arrArg argümanında tutulan değer arrParam parametresine kopyalanır.
Bu şekilde, dizinin elemanlarına olan referansları bellekte argümanlardan parametrelere kopyalayarak, bu parametrenin, argümanın işaret ettiği nesne ile aynı nesneye işaret ettiğini söylüyoruz:
En çok dikkatli olmamız gereken nokta da aslında burasıdır. Eğer çağrılan metot referans olarak geçirilen nesneyi değiştirirse, metotun çağrılması sonrasında kod yürütülmesi etkilenecektir (örnekte görüldüğü gibi, PrintArray() metotu başlangıçta geçirilen diziyi yazdırmıyor).
Temel ve referans türü argümanlar arasındaki farkı, onların geçirilme şekillerinde görüyoruz: Temel türler değerlerine göre geçirilir, ancak nesneler, referans olarak geçirilir.
9.9.2.4 Metot Argümanları Olarak İfadelerin Geçirilmesi
Bir metot çağrıldığında, argüman olarak bir ifadenin tamamını geçirebiliriz. Böylece, C# bu ifadelerin değerlerini hesaplar ve metot çağrıldığında kod yürütülmesi anında (eğer mümkünse bu derleme zamanında yapılır) ifadenin yerine sonucunu koyar. Aşağıdaki kod, argüman olarak ifadelerin geçirildiği metotların çağrılmasını göstermektedir.
PrintSign(2 + 3);
float oldQuantity = 3;
float quantity = 2;
PrintMax(oldQuantity * 5, quantity * 2);
|
Bu metotların yürütmesi sonucu:
Pozitif
En büyük sayı: 15.0
|
Parametreli bir metot çağrıldığında, önümüzdeki birkaç bölümde açıklanacak olan bazı belirli kuralların farkında olmalıyız.
9.9.2.5 Parametre Türü ile Uyumlu Olan Argümanların Geçirilmesi
Yalnızca metotun parametre listesinde bildirilen ilgili parametre türü ile uyumlu olan argümanların geçirilebileceğini bilmeliyiz.
Eğer örneğin, bir metot bildiriminde beklenen parametre float türünde bir kayan noktalı sayı ise, metot çağrıldığında int türünde bir değer geçirebilirsiniz. Türü derleyici tarafından float (kayan noktalı) değere dönüştürülmüş olacaktır, ve sonrasında yürütülmesi için metota geçirilecektir.
static void PrintNumber(float number)
{
Console.WriteLine("float sayı: {0}", number);
}
static void Main()
{
PrintNumber(5);
}
|
Örnekte, Main() metotu içinde PrintNumber() metotunun çağrılmasıyla birlikte, ilk olarak int türündeki 5 sabiti ilgili kayan nokta değeri olan 5.0f olarak dönüştürülür. Dönüştürülen değer sonrasında PrintNumber() metotuna geçirilir.
Beklendiği gibi, bu kod yürütmesinin sonucu şöyle olur:
9.9.2.6 Metot Parametresi ve Geçirilen Değerin Uyumluluğu
Argüman olarak geçirilen ifadenin hesaplanmış sonucu, bildirilen parametre türü ile aynı veya uyumlu bir türde olması gerekir (yukarıdaki paragrafa bakınız).
Öyleyse, eğer float türünde bir parametre gerekiyorsa, int türündeki bir ifade için hesaplanan değeri geçirebilirsiniz. Örneğin yukarıdaki örnekte, PrintNumber(5) metotunu eğer, 5 yerine 2+3 ifadesi ile çağırsaydık, bu ifadenin hesaplanan sonucu, (metotun beklediği gibi) float, kayan noktalı tür yada veri kaybı olmaksızın float türüne dönüştürülecek bir türden (bizim durumumuzda int) olması gereklidir. Bu nedenle, az önce açıkladıklarımızı göstermek için yukarıdaki paragrafta bulunan Main() metotu üzerinde biraz değişiklik yapalım:
static void Main()
{
PrintNumber(2 + 3);
}
|
Bu örnekte, ilk olarak toplama işlemi yürütülür. Daha sonra, tamsayı sonucu olan 5 kayan noktalı 5.0f eşdeğerine dönüştürülür. Bu da bittiğinde, PrintNumber(…) metotu, 5.0f argümanı ile çağrılır. Sonuç yine şöyle olur:
9.9.2.7 Argüman Türlerinin Bildirilme Sırasını Tutmak
Çağrılma zamanında, metota geçirilen değerlerin sırası, parametre listesinde bildirilen aynı sırada olmalıdır. Bunun nedeni, yukarıda belirtilen metot imzasından kaynaklanmaktadır.
Bunu açıklığa kavuşturmak için, aşağıdaki örneği ele alalım: PrintNameAndAge() metotunun bildiriminde parametre listesi string ve int türlerinde parametreler ile aşağıda gösterildiği gibi sıralanmıştır:
Person.cs
|
class Person
{
static void PrintNameAndAge(string name, int age)
{
Console.WriteLine("Ben {0}, {1} yaş(lar)ındayım.", name, age);
}
}
|
Sınıfımıza bir Main() metotu ekleyelim, bu metotun içinde PrintNameAndAge() metotunu çağıracağız. Şimdi parametreleri (tür olarak) ters sırada geçirmeyi deneyelim, böylece "John" ve 25 yerine, 25 ve "John" geçireceğiz:
static void Main()
{
// Wrong sequence of arguments
Person.PrintNameAndAge(25, "John");
}
|
Bu durumda derleyicinin int ve string sırasında parametre kabul eden PrintNameAndAge denilen bir yöntemi bulması mümkün olamaz. Derleyici bir hata bildirir:
The best overloaded method match for 'Person.PrintNameAndAge(string, int)' has some invalid arguments
|
9.9.3 Değişen Argüman Sayısı
Şimdiye kadar, metot için bildirilen parametre listesinin, çağrılma esnasında, bu metota geçirilen argümanların sayısı ile örtüştüğü metotların bildirimini incelemiştik.
Şimdi ise, çağrı yapıldığında, çağrılan kodun ihtiyaçlarını karşılamak için, metotun değişen sayıda nasıl argüman alabilecek şekilde bildirileceğini göreceğiz. Bu tür metotlar genellikle değişen sayıda argüman alan metotlar olarak adlandırılır.
Belli bir dizi halinde verilen kitap fiyatlarının toplamını hesaplayan bir örneğe bakalım, yukarıda bu zaten açıklanmıştı. Şimdiki örneğimize bir parametre olarak, seçilen kitapların fiyatlarından oluşan ondalık türünde bir diziyi geçireceğiz:
static void PrintTotalAmountForBooks(decimal[] prices)
{
decimal totalAmount = 0;
foreach (decimal singleBookPrice in prices)
{
totalAmount += singleBookPrice;
}
Console.WriteLine(
"Kitapların toplam miktarı:" + totalAmount);
}
|
Bu şekilde tanımlandığında, metotun her zaman çağrılmasından önce, bir ondalık sayı (decimal) dizisi oluşturmuş olacağımız ve belli değerler ile bunun başlatılmış olacağı varsayılıyor.
Aynı türden parametrelerin listesi her geçirildiğinde, bu değerlerden oluşan bir diziyi geçirmek yerine, onları virgülle ayrılmış olan argümanlar olarak doğrudan geçirmek için, değişen sayıda parametre kabul eden bir C# metotu oluşturduk.
Kitap örneğimizde, sadece bu yöntemi çağırmak için yeni bir dizi yaratmalıyız:
decimal[] prices = new decimal[] { 3m, 2.5m };
PrintTotalAmountForBooks(prices);
|
Ancak (birazdan göreceğimiz gibi) metot bildirimine birtakım kod ekleyerek, argüman olarak, kitap fiyatlarının bir listesini doğrudan geçirmemiz mümkün olur.
PrintTotalAmountForBooks(3m, 2.5m);
PrintTotalAmountForBooks(3m, 5.1m, 10m, 4.5m);
|
Böyle bir çağrı, ancak eğer bu metot, değişen sayıda argüman kabul edecek şekilde bildirildiği takdirde mümkün olur.
9.9.3.1 Değişen Sayıda Argüman Alan Bir Metot Nasıl Bildirilir?
Biçimsel olarak, değişen sayıda argüman alan metotun bildirimi, herhangi bir diğer metotun bildirimi ile aynıdır:
static (
)
{
// Metot’un gövdesi
}
|
Fark, aşağıda gösterildiği gibi,
nin params anahtar sözcüğü ile bildirilmesindedir.
=
[ [, i> i>], params [] ]
i= 2, 3, …
|
Liste bildirimindeki son eleman -
, metotun her çağrısı için türünde değişen sayıda argümanın geçirilmesine izin verir.
Bu elemanın bildiriminde türünden önce params eklemeliyiz: "params []". herhangi bir temel yada referans türü olabilir.
Metotun parametre listesindeki parametresinden önce bulunan
diğer elemanlar için geçerli olan kurallar ve özel özellikler, "Metot Parametreleri" Bölümü’nde anlattıklarımız ile aynıdır.
Buraya kadar anlatılanları açıklığa kavuşturmak için, değişen sayıda argüman alan bir metotun bildirimi ve çağrısı ile ilgili bir örneği tartışacağız:
static long CalcSum(params int[] elements)
{
long sum = 0;
foreach (int element in elements)
{
sum += element;
}
return sum;
}
static void Main()
{
long sum = CalcSum(2, 5);
Console.WriteLine(sum);
long sum2 = CalcSum(4, 0, -2, 12);
Console.WriteLine(sum2);
long sum3 = CalcSum();
Console.WriteLine(sum3);
}
|
Örnekte sayıları önceden bilinmeyen tamsayıların toplamı alınmaktadır. Metot bir, iki yada daha fazla parametrenin yanı sıra hiçbir parametre ile de çağrılabilir. Örnek kod yürütüldüğünde, aşağıdaki sonuç elde edilmiştir:
9.9.3.2 Değişen Sayıda Argümanlar : Diziler ve “params”
Metot çağrısı sırasında değişen sayıda argümanların geçirilmesine izin veren - parametresi, yukarıda verilen biçimsel tanımında, aslında türünden bir dizinin adıdır. Metotun çağrılmasıyla, türünden yada metota geçirilen herhangi bir uyumlu türden argümanlar (sayılarına önem verilmeksizin) bu dizinin içinde saklanacaktır. Daha sonra, metotun gövdesinde bunlar kullanılır. Bu parametrelerin erişimi ve ele alınışı diziler ile çalıştığımız şekilde gerçekleşir.
Daha açıklayıcı olmak için, seçili kitap fiyatlarının toplamını hesaplayan metotu, değişen sayıda argüman alacak şekilde değiştirmeliyiz:
static void PrintTotalAmountForBooks(params decimal[] prices)
{
decimal totalAmount = 0;
foreach (decimal singleBookPrice in prices)
{
totalAmount += singleBookPrice;
}
Console.WriteLine("Kitapların toplam miktarı:" +
totalAmount);
}
|
Görüldüğü gibi tek değişiklik, prices dizisinin decimal[] öncesinde yapılan params bildirimidir. Metot gövdesinde “prices” hâlâ decimal türünde bir dizidir, bu nedenle daha önce bildirdiğimiz gibi aynı şekilde kullanılıyor.
Şimdiki metotumuzu, önceden bir sayı dizisi bildirmeye ve onu argüman olarak geçirmeye gerek duymaksızın çağırabiliriz.
static void Main()
{
PrintTotalAmountForBooks(3m, 2.5m);
PrintTotalAmountForBooks(1m, 2m, 3.5m, 7.5m);
}
|
İki çağrının sonucu şöyle olacaktır:
Kitapların toplam miktarı: 5.5
Kitapların toplam miktarı: 14.0
|
prices bir dizi olduğundan, metot çağrısından önce dizinin bildirilip ilk değerlerle başlatılabileceği varsayılmaktadır. Daha sonra, o diziyi argüman olarak geçirmek için:
static void Main()
{
decimal[] pricesArr = new decimal[] { 3m, 2.5m };
// Passing initialized array as var-arg:
PrintTotalAmountForBooks(pricesArr);
}
|
Yukarıdaki çağrı geçerlidir ve kod yürütülmesinden sonra sonuç şöyle olacaktır:
Kitapların toplam miktarı: 5.5
|
9.9.3.3 Değişen Sayıda Argüman Alan Bir Metotun Konumu ve Bildirimi
Değişen sayıda argüman alan bir metot, parametre listesinde diğer parametreleri de kabul edebilir.
Aşağıdaki kod, örneğin, ilk parametre olarak string türünde bir eleman alır, ve daha sonra int türünde bir veya daha fazla parametre alabilir:
static void DoSomething(string strParam, params int[] x)
{
}
|
Düşünmemiz gereken tek şey, metot tanımındaki parametre listesinde, değişen sayıda argüman geçişine izin veren eleman, her zaman parametre listesinin sonuna eklenmelidir.
|
Metot çağrıldığında değişen sayıda argüman geçişine izin veren eleman, her zaman parametre listesinin sonunda bildirilmelidir.
|
Eğer öyleyse, en son örnekteki x parametresinin bildirimini gösterildiği gibi en son yere koymazsak:
static void DoSomething(params int[] x, string strParam)
{
}
|
Derleyici aşağıdaki hata iletisini döndürür:
9.9.3.4 Değişen Sayıda Argümanların Sayısı Üzerindeki Sınırlamalar
Değişen sayıda argümanlı metotlar için bir başka sınırlama da, metot bildiriminde değişen sayıda argümanın geçişine izin veren birden çok sayıda parametrenin olamamasıdır. Bu nedenle, eğer aşağıdaki şekilde bildirilmiş bir metotu derlemeye çalışırsak:
static void DoSomething(params int[] x, params string[] z)
{
}
|
Derleyici bildiğimiz hata iletisini döndürür:
A parameter array must be the last parameter in a formal parameter list
|
Bu kural, değişen sayıdaki argümanların parametre listesinin sonunda olması ile ilgili kuralın bir özel durumudur.
9.9.3.5 Boş Parametre Listesinin Özellikleri
Değişen sayıda argümanlı metotların bildirimi ve çağırmasını öğrendikten sonra, bir soru daha ortaya çıkıyor. Böyle bir metotu, herhangi bir parametre olmaksızın çağırsaydık ne olurdu?
Örneğin, kitap fiyatlarının toplamını hesaplayan metotumuzu herhangi bir kitabı sevmemek ve seçmemek durumunda çağırmamızın sonucu ne olurdu?
static void Main()
{
PrintTotalAmountForBooks();
}
|
Görüleceği gibi bu kod hatasız derlenir ve yürütülmesinden sonra sonuç aşağıdaki gibidir:
Kitapların toplam miktarı: 0
|
Bunun gerçekleşmesine neden, metotumuza çağrı esnasında herhangi bir değer geçirmiyor olsak da, decimal[] prices dizisi oluşturulur, ancak boştur (yani herhangi bir eleman içermez).
Bunu hatırlamakta fayda vardır, çünkü diziyi başlatmamış olsak dahi, C# parametreleri tutan dizi için bunu gerçekleştirir.
9.9.3.6 Değişen Sayıda Argümanlı Metot – Örnek
Değişen sayıda argümanlı metotları nasıl tanımlayacağımızı akılda tutarak, bir C# programının Main() metotunu şu şekilde yazabiliriz:
static void Main(params string[] args)
{
// Method body comes here
}
|
Yukarıdaki tanım geçerlidir ve derleyici tarafından herhangi bir hata olmadan kabul edilir.
9.9.4 İsteğe Bağlı Parametreler ve Adlandırılmış Argümanlar
Adlandırılmış argümanlar ve isteğe bağlı parametreler, C# dilinin iki farklı işlevidir. Ancak, onlar sık sık birlikte kullanılır. Bu parametreler C# sürüm 4. 0'de kullanılmaya başlanmıştır. İsteğe bağlı parametreler bir metot çağrıldığında bazı parametrelerin atlanmasına izin verir. Adlandırılmış argümanlar, metot parametre değerlerinin parametre listesindeki tam konumları yerine, adları tarafından atanmasına izin verir. C# dili sözdiziminin bu iki özelliği parametrelerinin farklı kombinasyonu ile bir metotu çağırdığımız durumlarda çok kullanışlıdır.
İsteğe bağlı parametrelerin bildirimi, aşağıda gösterildiği gibi varsayılan değer kullanılarak yapılabilir:
static void SomeMethod(int x, int y = 5, int z = 7)
{
}
|
Yukarıdaki örnekte, y ve z isteğe bağlıdır ve metot çağrısı sırasında atlanabilir:
static void Main()
{
// Normal call of SomeMethod
SomeMethod(1, 2, 3);
// Omitting z - equivalent to SomeMethod(1, 2, 7)
SomeMethod(1, 2);
// Omitting both y and z – equivalent to SomeMethod(1, 5, 7)
SomeMethod(1);
}
|
Metota, parametre adını takiben, iki nokta üst üste ve parametre değerini atayarak, belirli bir parametre adı ile değer geçirebiliriz. Bu tarzda adlandırılmış argümanların kullanıldığı bir örnek aşağıda gösterilmiştir:
static void Main()
{
// Passing z by name and x by position
SomeMethod(1, z: 3);
// Passing both x and z by name
SomeMethod(x: 1, z: 3);
// Reversing the order of the arguments passed by name
SomeMethod(z: 3, x: 1);
}
|
Yukarıdaki örnekteki bütün çağrılar birbirine denktir – y parametresi atlanmıştır, x ve z, sırasıyla 1 ve 3’e ayarlanmıştır. İkinci ve üçüncü çağrı arasındaki tek fark, parametre değerleri metota geçirilen aynı sırada hesaplanırken, son çağrıda 3 değerinin 1’den önce hesaplanacak olmasıdır. Bu örnekte tüm parametrelerin değeri sabittir ve bunların amacı sadece, adlandırılmış ve isteğe bağlı parametreler fikrini açıklamaktır. Ancak söz konusu husus, parametrelerin hesaplama sırası önemli olduğunda beklenmeyen davranışlara neden olabilir.
9.9.5 Metot Aşırı Yüklenmesi (Overloading)
Bir sınıf içinde bildirilen bir metotun adı başka bir metotun adı ile çakışıyorsa, ancak bu iki metotun paramere listeleri farklılık gösterdiği için (metot parametrelerinin sayısı veya onların düzenleniş şekli) imzaları değişikse, bu metotların farklı varyasyonları vardır veya bu metotlar aşırı yüklenmiştir deriz.
Örnek olarak, diyelim ki ekrana harfler ve rakamlar çizen bir program yazmak zorunda olduğumuzu varsayalım. Programımızın dizeler için DrawString(string str), tamsayılar için DrawInt(int number) ve kayan noktalı sayılar için DrawFloat(float number) çizim metotları – vb. olduğunu varsayabiliriz:
static void DrawString(string str)
{
// Draw string
}
static void DrawInt(int number)
{
// Draw integer
}
static void DrawFloat(float number)
{
// Draw float number
}
|
Görüldüğü gibi C# dili bize aşırı yüklenmiş adı verilen aynı Draw(...) metotunun varyasyonlarını oluşturmamızı sağlıyor. Aşağıdaki metot ekranda ne yazmak istediğimize bağlı olarak farklı parametre kombinasyonlarını almaktadır:
static void Draw(string str)
{
// Draw string
}
static void Draw(int number)
{
// Draw integer
}
static void Draw(float number)
{
// Draw float number
}
|
Yukarıdaki metotların tanımları geçerlidir ve hata mesajları olmadan derlenir. Draw(…) metotuna aşırı yüklenmiş denir.
9.9.5.1 Metot Parametreleri ve Metot İmzası
Yukarıda da belirtildiği gibi, bir C# metotunun imzasını belirlemek için gerekli sadece iki şey vardır: parametre türü ve parametrelerin listelenen sırası. Parametrelerin adı metot bildirimi için önemli değildir.
|
Bir C# metotunun kesin bildirimini oluştururken en önemli unsur, metot imzasının tanımı ve özellikle metot parametrelerinin türleridir.
|
Örneğin C#’de, aşağıdaki iki tanımlama aslında tek ve aynı metotu bildirir. Bunun nedeni parametrelerin her biri aynı parametre türünden olduğu içindir – int ve float. Yani kullandığımız değişkenlerin adları – param1 ve param2 veya p1 ve p2 önemli değildir.
// These two lines will cause an error
static void DoSomething(int param1, float param2) { }
static void DoSomething(int p1, float p2) { }
|
Eğer bir sınıfta yukarıda gösterildiği gibi iki veya daha fazla metotu bildirirsek, derleyici aşağıdaki gibi bir hata mesajı gösterecektir:
Type '' already defines a member called 'DoSomething' with the same parameter types.
|
Eğer parametre listesinde verilen bir pozisyondaki parametrenin türünü farklı bir tür ile değiştirirsek, C# bunları kesinlikle farklı iki yöntem olarak, veya daha doğrusu, aynı adı taşıyan metotların farklı varyasyonları olarak sayacaktır.
Örneğin, ikinci metotun parametre listesindeki ikinci parametre – float p2 türü float olarak değil fakat int olarak bildirildiyse, iki farklı imza ile iki farklı metot olacaktır – DoSomething(int, float) ve DoSomething(int, int). Şimdi imzalarının ikinci unsuru olan – parametre listesi, ikinci parametrenin farklı türde olması nedeniyle farklı hale gelmiştir:
static void DoSomething(int p1, float p2) { }
static void DoSomething(int param1, int param2) { }
|
Bu durumda parametreleri için aynı adı yazsak bile, derleyici pratikte farklı yöntemler oldukları için bu bildirimi kabul edecektir:
static void DoSomething(int param1, float param2) { }
static void DoSomething(int param1, int param2) { }
|
Derleyici eğer bu yöntemin iki varyasyonunu bildirmişsek de, kodu yine kabul edecektir, ancak bu sefer parametrelerin türlerini değil, sıralarını değiştiriyoruz.
static void DoSomething(int param1, float param2) { }
static void DoSomething(float param2, int param1) { }
|
Yukarıdaki örnekte parametre türlerinin sırası farklıdır ve bu imzayı da farklı kılar. Parametre listeleri farklı olduğundan, metot adının (DoSomething) her iki metot için aynı olması hiçbir rol oynamaz. Hala her iki yöntem için farklı imzalar vardır.
9.9.5.2 Aşırı Yüklenmiş Metotları Çağırma
Aynı adı taşıyan ve farklı imzaya sahip olan metotları bildirdikten sonra, her birini herhangi bir başka metot gibi çağırabiliriz - sadece adını ve argümanlarını kullanarak. İşte bir örnek:
static void PrintNumbers(int intValue, float floatValue)
{
Console.WriteLine(intValue + "; " + floatValue);
}
static void PrintNumbers(float floatValue, int intValue)
{
Console.WriteLine(floatValue + "; " + intValue);
}
static void Main()
{
PrintNumbers(2.71f, 2);
PrintNumbers(5, 3.14159f);
}
|
Kod çalıştırıldığında, göreceksiniz ki, ilk çağırma ikinci metota referans etmekte, ve ikinci çağırma ilk metota başvuru yapmaktadır. Çağrılacak metot kullanılan parametrelerin türüne bağlıdır. Yukarıdaki kod çalıştırıldıktan sonra sonuç:
Ancak, aşağıdaki satırlar derlenmez ve çalıştırılmaz:
static void Main()
{
PrintNumbers(2, 3);
}
|
Bunun çalışmamasının nedeni, derleyici iki tamsayıyı PrintNumbers adındaki herhangi bir metota geçirmeden önce, uygun türlere dönüştürmeye çalışır. Ancak, bu durumda bu dönüşümler eşit değildir. İki olası seçenek var - ya ilk parametreyi float türüne dönüştürmek ve PrintNumbers(float, int) metotunu çağırmak yada ikinci parametreyi float türüne dönüştürmek ve PrintNumbers(int, float) metotunu çağırmak. Bu belirsizlik el ile çözülmelidir, ve bunu yapmak için bir örnek aşağıda gösterilmiştir:
static void Main()
{
PrintNumbers((float)2, (short)3);
}
|
Yukarıdaki kod hatasız derlenmiş olacaktır, çünkü argümanlar dönüştürüldükten sonra, hangi metota referans verileceği açıkça belirgindir – PrintNumbers(float, int).
9.9.5.3 Çakışan İmzalı Metotlar
Metotları nasıl kullanacağımızı gösteren diğer bazı ilginç örnekleri tartışacağız. Yeniden tanımlanmaya (aşırı yüklenme) verilen yanlış bir örneğe göz atalım:
static int Sum(int a, int b)
{
return a + b;
}
static long Sum(int a, int b)
{
return a + b;
}
static void Main()
{
Console.WriteLine(Sum(2, 3));
}
|
Örnek kod derleme işlemi sırasında bir hata iletisi gösterir, çünkü aynı parametre listesine sahip (yani, aynı imzalı), ancak farklı sonuç döndüren iki metot vardır. Metot çağırmayı belirsiz hale getireceğinden buna derleyici tarafından izin verilmez.
9.9.6 Farklı Boyutta Üçgenler – Örnek
Şimdi biraz daha karmaşık bir örnek vermek için iyi bir zamana geldik, çünkü parametreli metotların nasıl bildirileceğini, onları nasıl çağıracağımızı ve bunun yanında metotlardan nasıl geri sonuç elde edeceğimizi biliyoruz. Aşağıda gösterildiği gibi konsol üzerinde üçgenler yazdıran bir program yazmak istediğimizi varsayalım:
n = 5
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 4
1 2 3
1 2
1
n = 6
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5
1 2 3 4 5 6
1 2 3 4 5
1 2 3 4
1 2 3
1 2
1
|
Bu görevin olası bir çözümü aşağıda verilmiştir:
Triangle.cs
|
using System;
class Triangle
{
static void Main()
{
// Entering the value of the variable n
Console.Write("n = ");
int n = int.Parse(Console.ReadLine());
Console.WriteLine();
// Printing the upper part of the triangle
for (int line = 1; line <= n; line++)
{
PrintLine(1, line);
}
// Printing the bottom part of the triangle
// that is under the longest line
for (int line = n - 1; line >= 1; line--)
{
PrintLine(1, line);
}
}
static void PrintLine(int start, int end)
{
for (int i = start; i <= end; i++)
{
Console.Write(i + " ");
}
Console.WriteLine();
}
}
|
Bu örnek kodun nasıl çalıştığını tartışalım. Üçgenleri ayrı satırlarda yer alan sayı dizileri gibi düşünmeliyiz, çünkü her satırı doğrudan konsola yazdırabilirsiniz. Konsolda üçgenin her bir satırını yazdırmak için bir araca ihtiyacımız var. Bu amaç için PrintLine(…) metotunu oluşturduk.
for-döngüsü kullanan bu metot, ardışık sayılardan oluşan bir satır yazmaktadır. Bu dizinin ilk sayısı metotun parametre listesindeki ilk parametredir (start değişkeni). Dizinin son elemanı, metota geçirilen ikinci parametredir (end değişkeni).
Dikkat etmelisiniz ki, sayılar ardışık olduğu için her satırın uzunluğu (sayı adedi), metotun parametre listesindeki ikinci parametresi (end) ile ilk parametresi (start) arasındaki farka karşılık gemektedir (üçgenleri oluşturacağınız zaman bunun faydasını göreceksiniz).
Sonra üçgenler yazdıran bir algoritmayı Main() metotu içinde uyguluyoruz. int.Parse adında başka bir metot ile de n değişkenini alıyor ve boş bir satır yazdırıyoruz.
Şimdi ardışık iki for-döngüsü ile girilen n değerine göre üçgeni oluşturuyoruz. Birinci döngüde üçgenin üst kısmını oluşturan satırları ve en ortadaki (en uzun) satırı da dahil eden tüm satırları yazdırıyoruz. İkinci döngüde orta satırın altında kalan ve üçgenin geri kalan tüm satırlarını yazdırıyoruz.
Yukarıda da belirttiğimiz gibi, satır numarası, uygun satır üzerinde yer alan eleman sayısına karşılık gelir. Ve her zaman 1’den başladığımız için, satır numarası her zaman o satırda yazılması gereken dizideki son elemana eşit olacaktır. Bu nedenle parametrelerinde tam olarak bu bilgiyi gerektirdiği için PrintLine(…) çağırdığınızda bunu kullanabilirsiniz.
Unutmayın ki, her bir sonraki satır üzerinde bulunan elemanların sayısı bir artmaktadır ve her satırın son elemanı önceki satırın son elemanından daha büyük olmalıdır. Bu nedenle birinci döngünün her tekrarında PrintLine(…) metotuna ilk parametre olarak 1 ve ikinci parametre olarak - line değişkeninin değerini geçiyoruz. Döngü gövdesinin her yürütmesinde line bir arttığı için, PrintLine(…) metotunun her yinelenmesinde önceki satırdan bir daha fazla elemana sahip satırın yazdırıldığını görmekteyiz.
Üçgenin orta sınırının altında kısmını çizen ikinci döngüde, ters mantık takip ediliyor. Satırlar aşağıya doğru çizildikçe, gitgide daha kısa satırları yazdırıyoruz. Her satır önceki satıra göre bir eleman azalıyor. Dolayısıyla, ikinci döngüde line değişkeni için başlangıç değerini : line = n – 1 olarak ayarlıyoruz. Döngünün her yinelenmesinde, line değeri bir azaltılır ve bu değer PrintLine(…) metotuna ikinci parametre olarak geçirilir.
Üçgeni yazdıran mantığı ayrı bir metot içinde uygulayarak programı geliştirebiliriz. Üçgeni yazdıran mantığın açıkça tanımlanmış olduğunu fark edebilirsiniz, bu nedenle değerini klavyeden aldığımız bir parametreli bir metotu bildirmekteyiz ve Main() metotundan onu çağırmaktayız.
static void Main()
{
Console.Write("n = ");
int n = int.Parse(Console.ReadLine());
Console.WriteLine();
PrintTriangle(n);
}
static void PrintTriangle(int n)
{
// Printing the upper part of the triangle
for (int line = 1; line <= n; line++)
{
PrintLine(1, line);
}
// Printing the bottom part of the triangle
// that is under the longest line
for (int line = n - 1; line >= 1; line--)
{
PrintLine(1, line);
}
}
|
Programı çalıştırdığınızda n için 3 değerini girerseniz, aşağıdaki sonucu alırsınız:
Dostları ilə paylaş: |