7. Sekali Lagi Metode Dan Kelas
Bab ini kembali
akan membahas kelas dan metode, diawali dengan menjelaskan tentang bagaimana
mengendalikan akses terhadap anggota suatu kelas. Bab ini kemudian
mendiskusikan pelewatan objek dan pengembalian objek sebagai nilai balik,
mengoverload metode, berbagai bentuk Main(),
rekursi, dan penggunaan katakunci static.
Pengendalian
Akses Terhadap Anggota Kelas
Meskipun
pendekatan C# sedikit lebih rumit, namun pada intinya, ada dua tipe dasar dari
anggota kelas: public dan private. Anggota publik dapat dengan
bebas diakses oleh kode yang didefinisikan di luar kelasnya. Ini merupakan tipe
anggota kelas yang telah digunakan sejauh ini. Anggota privat hanya dapat
diakses oleh metode yang didefinisikan di dalam kelasnya. Melalui penggunaan
anggota privat inilah akses dikendalikan.
Pembatasan akses
terhadap anggota kelas merupakan bagian fundamental dalam pemrograman
berorientasi objek karena hal ini membantu untuk menghindarkan objek dari
penyalahgunaan. Dengan mengijinkan akses terhadap data privat hanya untuk
metode-metode yang didefinisikan di dalam kelasnya, Anda dapat mencegah
penugasan nilai yang tidak diinginkan pada data privat tersebut. Tidak
dimungkinkan bagi kode di luar kelas untuk secara langsung menetapkan nilai
dari anggota privat. Anda juga dapat mengendalikan secara tepat bagaimana dan
kapan data di dalam sebuah objek digunakan.
Pemodifikasi
Akses C#
Kendali akses
anggota dicapai melalui penggunaan empat pemodifikasi akses: public, private, protected, dan internal. Pada bab ini, yang dibahas
hanya public dan private. Pemodifikasi akses protected akan dibahas pada Bab 10
karena melibatkan pewarisan. Pemodifikasi internal
hanya bisa diterapkan pada assembly.
Ketika anggota
sebuah kelas dimodifikasi oleh penspesifikasi public, anggota itu dapat diakses oleh sembarang kode di dalam
program Anda. Hal ini mencakup semua metode yang didefinisikan di dalam kelas
lain.
Ketika anggota
suatu kelas dispesifikasi sebagai private,
maka anggota itu hanya dapat diakses oleh anggota lain di dalam kelas tersebut.
Jadi, metode yang didefinisikan di dalam kelas lain tidak dapat mengaksesnya.
Seperti dijelaskan pada Bab 5, jika tidak ada penspesifikasi akses yang
digunakan, anggota kelas tersebut secara default dispesifikasi private.
Penspesifikasi
akses ditempatkan di awal statemen deklarasi anggota. Berikut beberapa
contohnya:
public
string pesanError;
private
double balon;
private bool apakahError(byte status)
{ // ...
Untuk memahami
perbedaan antara public dan private, perhatikan program berikut:
// Akses public versus
private.
using System;
class KelasKu {
private
int alfa; // secara eksplisit private
int
beta; // akses private access secara default
public
int gamma; // akses public
// Beberapa metode untuk mengakses alfa dan
beta. Boleh bagi anggota
// dari suatu kelas untuk mengakses anggota
private sesama kelas.
public
void SetAlfa(int a) {
alfa = a;
}
public
int GetAlfa() {
return
alfa;
}
public
void SetBeta(int a) {
beta = a;
}
public
int GetBeta() {
return
beta;
}
}
class DemoAkses {
static
void Main() {
KelasKu ob = new KelasKu();
// Akses terhadap alfa dan beta diijinkan
melalui metode sesama kelas.
ob.SetAlfa(-99);
ob.SetBeta(19);
Console.WriteLine("ob.alfa adalah " + ob.GetAlfa());
Console.WriteLine("ob.beta adalah " + ob.GetBeta());
// Anda tidak bisa mengakses alfa atau beta
seperti ini:
// ob.alfa = 10; // Salah! alfa adalah
private!
// ob.beta = 9; // Salah! beta adalah
private!
// Diijinkan untuk secara langsung
mengakses gamma karena ia public.
ob.gamma = 99;
}
}
Keluaran program
adalah:
ob.alfa adalah -99
ob.beta adalah 19
Seperti yang
dapat Anda lihat, di dalam kelas KelasKu,
alfa dispesifikasi sebagai private, beta secara default private,
dan gamma dispesifikasi sebagai public. Karena alfa dan beta adalah private, maka keduanya tidak dapat
diakses oleh kode di luar kelasnya. Oleh karena itu, di dalam kelas DemoAkses,
keduanya tidak dapat diakses secara langsung. Keduanya, alfa dan beta, harus
diakses melalui metode public, seperti SetAlfa()
dan GetAlfa(). Sebagai contoh,
jika Anda menghapus simbol komentar dar awal baris berikut
//
ob.alfa = 10; // Salah! alfa adalah private!
Anda tidak akan
bisa mengkompilasi program ini karena terjadi pelanggaran akses. Meskipun akses
terhadap alfa olek kode di luar
kelas KelasKu tidak diijinkan,
beberapa metode di dalam KelasKu
dengan bebas dapat mengaksesnya, seperti SetAlfa()
dan GetAlfa(). Hal yang sama
berlaku untuk beta.
Poin kunci di
sini adalah: Sebuah anggota privat dapat digunakan secara bebas oleh
anggota-anggota lain dari kelas yang sama, tetapi tidak bisa diakses oleh kode
di luar kelas tersebut.
Menerapkan
Akses Publik Dan Privat
Penggunaan yang
tepat dari akses publik dan privat merupakan komponen kunci dalam keberhasilan
pemrograman berorientasi objek. Meskipun tidak ada aturan tegas mengenai hal
ini, berikut diberikan beberapa prinsip umum yang bisa dijadikan acuan:
·
Anggota suatu kelas yang hanya
digunakan di dalam kelas itu sendiri harus dideklarasikan privat.
·
Data istans yang harus dalam rentang
nilai tertentu harus dideklarasikan privat, sehingga metode publik bisa berperan
sebagai pemeriksa rentang.
·
Jika pengubahan suatu anggota dapat
menyebabkan pengaruh melebih anggota itu sendiri (yaitu, mempengaruhi aspek
lain dari objek), maka anggota itu harus privat dan akses terhadapnya harus
dikendalikan.
·
Anggota yang bisa membahayakan suatu
objek ketika disalahgunakan harus dideklarasikan privat. Akses terhadap anggota
ini harus melalui metode publik untuk mencegah penyalahgunaan.
·
Metode yang mendapatkan (get) dan
menetapkan (set) nilai dari data privat harus dideklarasikan publik.
Mengendalikan
Akses: Sebuah Studi Kasus
Untuk lebih
memahami “bagaimana dan mengapa” akses kendali dilakukan, berikut disajikan
sebuah studi kasus. Salah satu contoh penting dalam pemrograman beroerintasi
objek adalah sebuah kelas yang mengimplementasikan sebuah tumpukan. Seperti
yang mungkin Anda telah ketahui, tumpukan merupakan suatu struktur data yang
mengimplementasikan LIFO (last-in, firts-out). Nama tumpukan berasal dari
analogi tumpukan piring. Piring pertama pada meja merupakan piring terakhir
yang akan digunakan.
Tumpukan
merupakan contoh klasik dalam pemrograman berorientasi objek karena
mengkombinasikan penyimpanan informasi dengan beberapa metode yang mengakses
informasi tersebut. Jadi, tumpukan merupakan mesin data yang melaksanakan
pendekatan LIFO. Kombinasi semacam itu merupakan pilihan tepat bagi sebuah
kelas dimana di dalamnya beberapa metode untuk penyimpanan informasi
dideklarasikan privat, dan beberapa metode untuk mengakses informasi
dideklarasikan publik.
Tumpukan mendefinisikan
dua operasi dasar: push dan pop. Push menempatkan sebuah nilai di atas
tumpukan. Operasi pop mengambil atau menghapus sebuah nilai dari atas tumpukan.
Jadi, begitu suatu nilai dihapus dari tumpukan, ia tidak bisa diakses lagi.
Contoh yang ditampilkan
di sini menciptakan sebuah kelas bernama Tumpukan.
Penyimpanan untuk tumpukan disediakan oleh sebuah array privat. Operasi push
dan pop tersedia melaui beberapa metode publik pada kelas Tumpukan. Seperti yang ditunjukkan di sini, kelas Tumpukan menyimpan beberapa karakter,
tetapi mekanisme yang sama dapat pula dibuat untuk menyimpan sembarang tipe
data lain.
// Kelas Tumpukan untuk karakter.
using System;
class Tumpukan {
// Anggota-anggota
ini privat.
char[] tumpukan; // menampung tumpukan
int iat; // indeks dari atas tumpukan
// Menciptakan sebuah
Tumpukan kosong dengan ukuran tertentu.
public Tumpukan(int
ukuran) {
tumpukan = new char[ukuran]; // mengalokasikan
memori untuk tumpukan
iat = 0;
}
// Mendorong karakter
ke atas tumpukan.
public void Push(char
ch) {
if(iat==tumpukan.Length) {
Console.WriteLine(" -- Tumpukan penuh.");
return;
}
tumpukan[iat] = ch;
iat++;
}
// Menghapus sebuah
karakter dari atas tumpukan.
public char Pop()
{
if (iat == 0)
{
Console.WriteLine(" -- Tumpukan kosong.");
return (char)0;
}
iat--;
return tumpukan[iat];
}
// Menghasilkan true
jika tumpukan penuh.
public bool apaPenuh()
{
return iat == tumpukan.Length;
}
// Menghasilkan true
jika tumpukan kosong.
public bool apaKosong()
{
return iat == 0;
}
// Menghasilkan
kapasitas total dari tumpukan.
public int Kapasitas()
{
return tumpukan.Length;
}
// Menghasilkan
jumlah objek yang saat ini pada tumpukan.
public int GetJumlah()
{
return iat;
}
}
Akan diperiksa
program tersebut secara detil. Kelas Tumpukan
diawali dengan pendeklarasian dua variabel instans:
// Anggota-anggota ini
privat.
char[] tumpukan; // menampung
tumpukan
int iat; // indeks dari atas
tumpukan
Array tumpukan menyediakan tempat penyimpanan
untuk tumpukan, dimana pada kasus ini adalah tumpukan karakter. Perhatikan
bahwa tidak ada array yang dialokasikan. Pengalokasian array aktual ditangani
oleh konstruktor Tumpukan. Anggota iat memuat indeks dari atas tumpukan.
Kedua anggota iat dan tumpukan secara default private.
Hal ini untuk menegakkan mekanisme LIFO (last-in, first-out). Jika akses publik terhadap iat diijinkan, maka elemen-elemen pada
tumpukan dapat diakses secara tidak berurutan. Di samping itu, iat merupakan indeks dari elemen
teratas di dalam tumpukan, manipulasi terhadap iat oleh kode di luar kelas Tumpukan
harus dicegah agar menghindari perusakan tumpukan. Akses terhadap tumpukan dan iat disediakan secara tidak langsung bagi pengguna kelas Tumpukan melalui beberapa metode
publik.
Konstruktor Tumpukan ditampilkan berikut:
// Menciptakan sebuah
Tumpukan kosong dengan ukuran tertentu.
public Tumpukan(int ukuran) {
tumpukan = new char[ukuran]; // mengalokasikan memori untuk tumpukan
iat = 0;
}
Ukuran tumpukan
yang diinginkan dilewatkan kepada konstruktor. Konstruktor mengalokasikan array
dan menetapkan iat menjadi nol.
Jadi, nilai nol di dalam iat
mengindikasikan bahwa tumpukan kosong.
Metode publik Push() menempatkan sebuah elemen ke
atas tumpukan, ditunjukkan di sini:
// Mendorong karakter ke
atas tumpukan.
public void Push(char ch) {
if(iat==tumpukan.Length)
{
Console.WriteLine("
-- Tumpukan penuh.");
return;
}
tumpukan[iat] = ch;
iat++;
}
ELemen yang akan
ditempatkan ke atas tumpukan dilewatkan di dalam ch. Sebelum elemen tersebut ditambahkan
ke tumpukan, pemeriksaan dilakukan untuk memastikan apakah masih ada tempat di
dalam array. Ini dilakukan dengan memastikan bahwa iat tidak melebihi panjang tumpukan.
Jika masih ada tempat di dalam array, elemen tersebut disimpan di dalam tumpukan pada indeks yang dispesifikasi
oleh iat, dan kemudian iat diinkremen. Jadi, iat selalu memuat indeks dari elemen
berikutnya di dalam tumpukan.
Untuk menghapus
sebuah elemen dari tumpukan, Anda bisa memanggil metode publik Pop(), yang ditunjukkan di sini:
// Menghapus sebuah karakter
dari atas tumpukan.
public char Pop()
{
if
(iat == 0)
{
Console.WriteLine("
-- Tumpukan kosong.");
return
(char)0;
}
iat--;
return
tumpukan[iat];
}
Di sini, nilai
dari iat diperiksa. Jika ia bernilai
nol, maka tumpukan kosong. Sebaliknya, iat
didekremen, dan elemen pada indeks tersebut dijadikan nilai balik.
Meskipun Push() dan Pop() adalah dua metode yang diperlukan untuk mengimplementasikan
sebuah tumpukan, beberapa metode lain juga penting, dan kelas Tumpukan mendefinisikan empat metode
lainnya. Semua metode tersebut adalah apaPenuh(),
apaKosong(), Kapasitas(), dan GetJumlah().
Semua metode tersebut menyediakan informasi tentang keadaan tumpukan, yang
ditunjukkan di sini:
// Menghasilkan true jika
tumpukan penuh.
public bool
apaPenuh()
{
return
iat == tumpukan.Length;
}
// Menghasilkan true jika
tumpukan kosong.
public bool apaKosong()
{
return
iat == 0;
}
// Menghasilkan kapasitas
total dari tumpukan.
public int Kapasitas()
{
return
tumpukan.Length;
}
// Menghasilkan jumlah objek
yang saat ini pada tumpukan.
public int GetJumlah()
{
return
iat;
}
Metode apaPenuh() menghasilkan nilai balik true ketika tumpukan penuh dan false jika sebaliknya. Metode apaKosong() menghasilkan nilai balik true jika tumpukan kosong dan false jika sebaliknya. Untuk
mendapatkan kapasitas total tumpukan (yaitu, total jumlah elemen yang bisa
dimuat), Anda bisa memanggil metode Kapasitas().
Untuk mendapatkan jumlah elemen yang saat ini disimpan dalam tumpukan, Anda
bisa memanggila GetJumlah(). Semua
metode tersebut berguna karena informasi yang diberikan memerlukan akses
terhadap iat, yang privat. Ini
merupakan contoh bagaimana metode publik dapat mengakses anggota privat secara
aman.
Program berikut
mendemonstrasikan tumpukan:
// Demonstrasi kelas Tumpukan.
using System;
class DemoTumpukan {
static void Main() {
Tumpukan tumpukan1
= new Tumpukan(10);
Tumpukan
tumpukan2 = new Tumpukan(10);
Tumpukan
tumpukan3 = new Tumpukan(10);
char ch;
int i;
// Menempatkan
beberapa karakter pada tumpukan1.
Console.WriteLine("Menempatkan A sampai J pada tumpukan1.");
for(i=0; !tumpukan1.apaPenuh(); i++)
tumpukan1.Push((char) ('A' + i));
if(tumpukan1.apaPenuh())
Console.WriteLine("tumpukan1 penuh.");
// Menampilkan isi
dari tumpukan1.
Console.Write("Isi dari tumpukan1: ");
while( !tumpukan1.apaKosong() ) {
ch =
tumpukan1.Pop();
Console.Write(ch);
}
Console.WriteLine();
if(tumpukan1.apaKosong()) Console.WriteLine("tumpukan1 kosong.\n");
// Menempatkan
beberapa karakter lagi pada tumpukan1.
Console.WriteLine("Lagi menempatkan A sampai J pada tumpukan1.");
for(i=0; !tumpukan1.apaPenuh(); i++)
tumpukan1.Push((char) ('A' + i));
// Sekarang,
menghapus dari tumpukan1 dan menempatkan elemen pada tumpukan2.
// Ini menyebabkan
tumpukan2 menampung elemen elemen dengan urutan terbalik.
Console.WriteLine("Sekarang,
mengambil karakter dari tumpukan1 dan menempatkannya " +
"pada tumpukan2.");
while (!tumpukan1.apaKosong())
{
ch = tumpukan1.Pop();
tumpukan2.Push(ch);
}
Console.Write("Isi dari tumpukan2: ");
while (!tumpukan2.apaKosong())
{
ch =
tumpukan2.Pop();
Console.Write(ch);
}
Console.WriteLine("\n");
// Menempatkan 5
karakter pada tumpukan.
Console.WriteLine("Menempatkan 5 karakter pada tumpukan3.");
for (i = 0; i < 5; i++)
tumpukan3.Push((char)('A' + i));
Console.WriteLine("Kapasitas dari tumpukan3: " +
tumpukan3.Kapasitas());
Console.WriteLine("Jumlah objek pada tumpukan3: " +
tumpukan3.GetJumlah());
}
}
Keluaran program
ditampilkan di sini:
Menempatkan A sampai J pada tumpukan1.
tumpukan1 penuh.
Isi dari tumpukan1: JIHGFEDCBA
tumpukan1 kosong.
Lagi menempatkan A sampai J pada tumpukan1.
Sekarang, mengambil karakter dari tumpukan1 dan
menempatkannya pada tumpukan2.
Isi dari tumpukan2: ABCDEFGHIJ
Menempatkan 5 karakter pada tumpukan3.
Kapasitas dari tumpukan3: 10
Jumlah objek pada tumpukan3: 5
Melewatkan
Referensi Kepada Metode
Sampai sejauh
ini, contoh pada buku ini hanya menggunakan tipe nilai, seperti int atau double, sebagai parameter pada metode. Namun, sebenarnya tipe
referensi juga umum dipakai sebagai parameter. Sebagai contoh, perhatikan
contoh berikut:
// Referensi dapat dilewatkan kepada metode.
using System;
class KelasKu {
int alfa, beta;
public KelasKu(int i, int j)
{
alfa = i;
beta = j;
}
// Menghasilkan true
jika ob memuat nilai sama dengan objek pemanggil.
public bool SamaDengan(KelasKu ob)
{
if ((ob.alfa == alfa) & (ob.beta ==
beta))
return true;
else return false;
}
// Membuat salinan
dari ob.
public void Salin(KelasKu ob)
{
alfa = ob.alfa;
beta = ob.beta;
}
public void Tampil()
{
Console.WriteLine("alfa: {0}, beta: {1}",
alfa, beta);
}
}
class PelewatanObjek {
static void Main() {
KelasKu ob1 = new KelasKu(4, 5);
KelasKu ob2 = new KelasKu(6, 7);
Console.Write("ob1: ");
ob1.Tampil();
Console.Write("ob2: ");
ob2.Tampil();
if (ob1.SamaDengan(ob2))
Console.WriteLine("ob1 dan ob2 mempunyai nilai sama.");
else
Console.WriteLine("ob1 dan ob2 mempunyai nilai beda.");
Console.WriteLine();
// Sekarang,
membuat ob1 salinan dari obe2.
ob1.Salin(ob2);
Console.Write("ob1 setelah penyalinan: ");
ob1.Tampil();
if (ob1.SamaDengan(ob2))
Console.WriteLine("ob1 dan ob2 mempunyai nama sama.");
else
Console.WriteLine("ob1 dan ob2 mempunyai nilai beda.");
}
}
Keluaran program
ditampilkan di sini:
ob1: alfa: 4, beta: 5
ob2: alfa: 6, beta: 7
ob1 dan ob2 mempunyai nilai beda.
ob1 setelah penyalinan: alfa: 6, beta: 7
ob1 dan ob2 mempunyai nama sama.
Metode SamaDengan() dan Salin() masing-masing mengambil sebuah argumen bertipe KelasKu sebagai argumen. Metode SamaDengan() membandingkan nilai alfa dan beta di dalam objek pemanggil dengan nilai alfa dan beta di dalam
objek yang dilewatkan melalui ob. Metode
ini menghasilkan true hanya jika kedua objek memuat nilai sama. Metode Salin() menugaskan nilai alfa dan beta di dalam objek yang ditunjuk oleh ob kepada alfa dan beta di dalam objek pemanggil. Seperti
yang ditunjukkan pada contoh ini, secara sintaks, cara tipe referensi
dilewatkan kepada metode sama dengan pelewatan nilai.
Bagaimana
Argumen Dilewatkan
Seperti
didemonstrasikan pada contoh sebelumnya, pelewatan referensi objek kepada
metode merupakan hal yang cukup sederhana. Namun, ada beberapa aspek yang tidak
ditunjukkan pada contoh itu. Pada beberapa kasus, pengaruh pelewatan tipe
referensi akan berbeda dari pelewatan nilai. Untuk melihat bagaimana, akan
dibahas tentang dua cara pelewatan argumen.
Cara pertama
adalah pemanggilan-dengan-nilai. Metode ini menyalin nilai dari sebuah argumen
ke dalam parameter formal dari subrutin. Oleh karena itu, perubahan yang
dilakukan terhadap parameter subrutin tidak akan berpengaruh pada argumen. Cara
kedua adalah pemanggilan-dengan-referensi. Pada metode ini, sebuah referensi
yang menunjuk ke argumen dilewatkan kepada parameter. Di dalam subrutin,
referensi ini dipakai untuk mengakses argumen aktual yang dispesifikasi dalam
pemanggilan. Ini berarti bahwa perubahan yang dilakukan terhadap parameter akan
berpengaruh pada argumen yang digunakan untuk memanggil subrutin.
Secara default, C#
menggunakan pemanggilan-dengan-nilai, yang berarti bahwa salinan dari argumen
dibuat dan diberikan kepada parameter penerima. Jadi, ketika Anda melewatkan
sebuah tipe nilai, seperti int atau double, apa yang terjadi pada parameter
yang menerima argumen tidak berpengaruh di luar metode. Sebagai contoh,
perhatikan program berikut:
// Tipe nilai dilewatkan
dengan nilai.
using System;
class Test {
/* Metode ini tidak menyebabkan perubahan
terhadap argumen
yang digunakan dalam pemanggilan. */
public
void TidakBerubah(int i, int j) {
i = i + j;
j = -j;
}
}
class PemanggilanDenganNilai {
static
void Main() {
Test ob = new Test();
int
a = 15, b = 20;
Console.WriteLine("a dan b sebelum pemanggilan: " +
a + " " + b);
ob.TidakBerubah(a, b);
Console.WriteLine("a dan b setelah pemanggilan: " +
a + " " + b);
}
}
Keluaran program
ditunjukkan di sini:
a dan b sebelum pemanggilan: 15 20
a dan b setelah pemanggilan: 15 20
Seperti yang
dapat Anda lihat, beberapa operasi yang terjadi di dalam TidakBerubah() tidak berpengaruh pada nilai-nilai dari a dan b yang digunakan dalam pemanggilan. Ini karena salinan dari a dan b diberikan kepada parameter i
dan j, tetapi a dan b sama sekali
independen dari i dan j. Jadi, penugasan nilai baru pada i tidak akan mempengaruhi a.
Ketika Anda
melewatkan sebuah referensi kepada metode, situasi menjadi sedikit kompleks.
Pada kasus ini, referensi, itu sendiri, dilewatkan dengan nilai. Jadi, salinan
dari referensi dibuat dan perubahan terhadap parameter tidak akan berpengaruh
pada argumen. (Sebagai contoh, membuat parameter sekarang menunjuk ke sebuah
objek baru tidak akan mempengaruhi objek yang ditunjuk oleh argumen). Namun,
Anda perlu hati-hati, perubahan yang dilakukan terhadap objek yang ditunjuk
oleh parameter akan mempengaruhi objek yang ditunjuk oleh argumen. Akan Anda
lihat mengapa.
Ingat bahwa
ketika Anda menciptakan sebuah variabel bertipe kelas, Anda sama saja dengan
menciptakan sebuah referensi yang menunjuk ke objek dari kelas tersebut. Jadi,
ketika Anda melewatkan referensi kepada sebuah metode, parameter yang
menerimanya akan menunjuk ke objek sama yang ditunjuk oleh argumen. Oleh karena
itu, argumen dan parameter keduanya akan menunjuk ke objek yang sama. Jadi,
perubahan yang dilakukan terhadap objek di dalam metode akan berpengaruh pada
objek yang digunakan sebagai argumen. Sebagai contoh, perhatikan program
berikut:
// Objek dilewatkan dengan
referensi.
using System;
class Test
{
public
int a, b;
public
Test(int i, int j)
{
a = i;
b = j;
}
/* Melewatkan sebuah objek. Sekarang, ob.a
dan ob.b di dalam objek
yang digunakan dalam pemanggilan akan
berubah. */
public
void Berubah(Test ob)
{
ob.a = ob.a + ob.b;
ob.b = -ob.b;
}
}
class PemanggilanDenganRef {
static
void Main() {
Test ob = new Test(15, 20);
Console.WriteLine("ob.a dan ob.b sebelum pemanggilan:
" +
ob.a + " " + ob.b);
ob.Berubah(ob);
Console.WriteLine("ob.a dan ob.b setelah pemanggilan:
" +
ob.a + " "
+ ob.b);
}
}
Keluaran program
ditunjukkan di sini:
ob.a dan ob.b sebelum pemanggilan: 15 20
ob.a dan ob.b setelah pemanggilan: 35 -20
Seperti yang
dapat Anda lihat, pada kasus ini, beberapa operasi di dalam Berubah() mempengaruhi objek yang
digunakan sebagai argumen.
Menggunakan
Parameter ref dan out
Seperti yang
baru saja dijelaskan, tipe nilai, seperti int
atau char, dilewatkan dengan nilai
kepada metode. Ini berarti bahwa perubahan pada parameter penerima tipe nilai
tidak mempengaruhi argumen aktual yang dipakai dalam pemanggilan. Anda dapat,
bagaimanapun, mengubah watak ini. Melalui penggunaan katakunci ref dan out, Anda dimungkinkan untuk melewatkan sembara tipe nilai dengan
referensi. Dengan melakukannya, metode dapat mengubah argumen yang dipakai di
dalam pemanggilan.
Sebelum membahas
mengenai penggunaan ref dan out, Anda perlu memahami mengapa Anda
ingin melewatkan sebuah tipe nilai dengan referensi. Secara umum, ada dua
alasan: untuk mengijinkan metode untuk mengubah isi argumennya atau untuk
mengijinkan metode untuk menghasilkan lebih dari satu nilai balik. Akan dibahas
setiap alasan lebih detil lagi.
Seringkali Anda menginginkan
sebuah metode yang mampu beroperasi pada argumen aktual yang dilewatkan
kepadanya. Contoh penting dari kasus ini adalah metode Tukar() yang menukar nilai dari kedua argumennya. Karena tipe nilai
dilewatkan dengan nilai, adalah tidak mungkin untuk menulis sebuah metode yang
menukar nilai dari dua int,
misalnya, menggunakan mekanisme pelewatan dengan nilai default C#.
Seperti Anda
ketahui, statemen return memampukan
sebuah metode untuk menghasilkan nilai balik kepada pemanggilnya. Namun, sebuah
metode hanya dapat menghasilkan satu nilai balik setiap kali ia dipanggil.
Bagaimana jika Anda memerlukan dua atau lebih nilai balik? Sebagai contoh,
bagaimana jika Anda ingin menciptakan sebuah metode yang mendekomposisi sebuah
angka titik-mengambang menjadi komponen integer dan komponen fraksionalnya?
Untuk melakukan ini memerlukan dua informasi yang dijadikan nilai balik:
komponen integer dan komponen fraksional. Metode ini tidak dapat ditulis dengan
satu nilai balik. Pemodifikasi out
menyelesaikan masalah ini.
Menggunakan
ref
Pemodifikasi
parameter ref menyebabkan C# untuk
menciptakan pemanggilan-dengan-referensi, bukan pemanggilan-dengan-nilai.
Pemodifikasi ref dispesifikasi
ketika metode dideklarasikan dan ketika ia dipanggil. Akan dibahas mulai dengan
contoh sederhana. Program berikut menciptakan sebuah metode bernama Kuadrat() yang menghasilkan nilai balik
berupa kuadrat dari argumennya. Perhatikan kegunaan dari ref.
// Penggunaan ref untuk
melewatkan tipe nilai dengan referensi.
using System;
class TestRef {
// Metode ini mengubah argumennya. Perhatikan
penggunaan ref.
public
void Kuadrat(ref int i)
{
i = i * i;
}
}
class DemoRef {
static
void Main() {
TestRef ob = new TestRef();
int
a = 10;
Console.WriteLine("a sebelum pemanggilan: " + a);
ob.Kuadrat(ref a); // perhatikan penggunaan ref
Console.WriteLine("a setelah pemanggilan: " + a);
}
}
Perhatikan bahwa
ref mengawali keseluruhan deklarasi
parameter di dalam metode dan juga mengawali argumen ketika metode dipanggil.
Keluaran dari program ini, seperti yang ditampilkan, menegaskan bahwa nilai
dari argumen, a, termodifikasi oleh Kuadrat():
a sebelum pemanggilan: 10
a setelah pemanggilan: 100
Dengan
menggunakan ref, sekarang
dimungkinkan untuk menulis sebuah metode yang menukar nilai dari dua argumen
bertipe nilai. Sebagai contoh, berikut adalah program yang memuat sebuah metode
bernama Tukar() yang menukar nilai
dari kedua argumen integernya:
// Menukar dua nilai.
using System;
class TukarNilai {
// Metode ini sekarang mengubah argumennya.
public void Tukar(ref int
a, ref int b) {
int
t;
t = a;
a = b;
b = t;
}
}
class DemoTukarNilai {
static
void Main() {
TukarNilai ob = new TukarNilai();
int
x = 10, y = 20;
Console.WriteLine("x dan y sebelum pemanggilan: " + x
+ " " + y);
ob.Tukar(ref x, ref y);
Console.WriteLine("x dan y sesudah pemanggilan: " + x
+ " " + y);
}
}
Keluaran dari
program ini adalah
x dan y sebelum pemanggilan: 10 20
x dan y sesudah pemanggilan: 20 10
Hal penting
untuk memahami ref: Argumen yang
dilewatkan dengan ref harus ditugasi
sebuah nilai sebelum pemanggilan. Alasannya adalah bahwa metode yang menerima
argumen semacam itu berasumsi bahwa parameter menunjuk ke sebuah nilai valid.
Jadi, dengan menggunakan ref, Anda
tidak bisa menggunakan metode tersebut untuk memberikan nilai awal pada
argumen.
Menggunakan
out
Kadangkala Anda
ingin menggunakan sebuah parameter referensi untuk menerima nilai dari suatu
metode. Sebagai contoh, Anda mungkin memiliki sebuah metode yang melakukan
beberapa fungsi, seperti membuka socket jaringan, yang menghasilkan nilai balik
berupa status sukses/gagal di dalam parameter referensi. Masalah dengan
skenario ini adalah bahwa parameter ref
harus diinisialisasi sebelum pemanggilan. Jadi, untuk menggunakan sebuah
parameter ref, Anda perlu memberikan
nilai awal pada argumen. Solusi alternatif yang lebih baik: parameter out.
Parameter out sama dengan parameter ref dengan satu pengecualian: parameter
out hanya bisa dipakai untuk melewatkan sebuah nilai. Anda tidak perlu
memberikan nilai awal pada variabel yang digunakan sebagai parameter out sebelum pemanggilan metode. Di
samping itu, di dalam metode, parameter out
dipandang tidak memiliki nilai awal. Ini mengimplikasikan bahwa metode harus
menugaskan sebuah nilai kepada parameter. Jadi, setelah pemanggilan metode,
parameter out akan memuat sebuah
nilai.
Berikut
merupakan sebuah contoh yang menggunakan parameter out. Pada kelas Dekomposisi, metode GetKomponen() mendekomposisi angka
titik-mengambang menjadi komponen integer dan komponen fraksional. Perhatikan
bagaimana setiap komponen dijadikan nilai balik kepada pemanggil.
// Menggunakan out.
using System;
class Dekomposisi {
/* Mendekomposisi sebuah angka
titik-mengambang menjadi
komponen integer dan fraksionalnya. */
public
int GetKomponen(double n, out double frak) {
int
seluruh;
seluruh = (int)n;
frak = n - seluruh; // melewatkan
bagian fraksional kembali ke frak
return
seluruh; // menghasilkan nilai balik
}
}
class GunakanOut
{
static void Main()
{
Dekomposisi ob = new Dekomposisi();
int
i;
double
f;
i = ob.GetKomponen(10.125, out f);
Console.WriteLine("Komponen integer adalah " + i);
Console.WriteLine("Komponen fraksional adalah " + f);
}
}
Keluaran program
ditampilkan di sini:
Komponen integer adalah 10
Komponen fraksional adalah 0.125
Metode GetKomponen() menghasilkan dua
informasi. Pertama, bagian integer dari n
dikembalikan sebagai nilai balik. Kedua, bagian fraksional dari n dilewatkan kembali ke pemanggil
melalui parameter out, yaitu frak. Seperti yang ditunjukkan pada contoh
ini, dengan menggunakan out,
dimungkinkan bagi metode untuk menghasilkan dua nilai balik.
Tentu saja, Anda
tidak dibatasi hanya boleh menggunakan satu parameter out. Sebuah metode dapat menghasilkan sebanyak mungkin informasi
yang diperlukan melalui parameter out.
Berikut diberikan sebuah contoh yang menggunakan dua parameter out. Metode MempFaktBersama() melakukan dua fungsi. Pertama, ia menentukan
apakah kedua integer memiliki faktor bersama (selain 1). Metode ini
menghasilkan true jika ya dan false jika sebaliknya. Kedua, jika
keduanya mempunyai faktor bersama, MempFaktBersama()
akan menghasilkan faktor bersama terkecil dan faktor bersama terbesar di
dalam kedua parameter out.
// Menggunakan dua parameter out.
using System;
class Num {
/* Menentukan jika x
dan v mempunyai pembagi bersama.
Jika ya, maka
faktor bersama terbesar dan terkecil dikembalikan
dalam kedua
parameter out. */
public bool MempFaktBersama(int
x, int y,
out int terkecil, out int terbesar) {
int i;
int maks = x < y ? x : y;
bool pertama = true;
terkecil = 1;
terbesar = 1;
// Mencari faktor
bersama terkecil dan terbesar.
for (i = 2; i <= maks / 2 + 1; i++)
{
if (((y % i) == 0) & ((x % i) ==
0))
{
if (pertama)
{
terkecil = i;
pertama = false;
}
terbesar = i;
}
}
if (terkecil != 1) return true;
else return false;
}
}
class DemoOut
{
static void Main()
{
Num ob = new Num();
int lcf, gcf;
if (ob.MempFaktBersama(231, 105, out
lcf, out gcf))
{
Console.WriteLine("Lcf dari 231 dan 105 adalah " +
lcf);
Console.WriteLine("Gcf dari 231 dan 105 adalah " +
gcf);
}
else
Console.WriteLine("Tidak ada faktor bersama dari 35 dan 49.");
if (ob.MempFaktBersama(35, 51, out lcf,
out gcf))
{
Console.WriteLine("Lcf dari 35 dan 51 adalah " +
lcf);
Console.WriteLine("Gcf dari 35 dan 51 adalah " +
gcf);
}
else
Console.WriteLine("Tidak ada faktor bersama dari 35 dan 51.");
}
}
Dalam Main(), perhatikan bahwa lcf dan gcf tidak ditugasi nilai sebelum pemanggilan terhadap MempFaktBersama(). Akan menjadi error
jika parameter ref digunakan
menggantikan out. Metode ini
menghasilkan true atau false, bergantung pada apakah kedua
integer mempunyai faktor bersama atau tidak. Jika mempunyai, faktor bersama
terkecil dan terbesar dijadikan nilai balik di dalam parameter out. Keluaran program ditunjukkan di
sini:
Lcf dari 231 dan 105 adalah 3
Gcf dari 231 dan 105 adalah 21
Tidak ada faktor bersama dari 35 dan 51.
Menggunakan
ref Dan out Pada Referensi
Penggunaan ref dan out tidak dibatasi hanya pada pelewatan tipe nilai. Keduanya juga
bisa dipakai ketika sebuah referensi dilewatkan. Ketika ref atau out
memodifikasi sebuah referensi, ini menyebabkan metode dapat mengubah objek yang
ditunjuk oleh referensi, Perhatikan program berikut, yang menggunakan parameter
referensi ref untuk menukar dua
objek yang ditunjuk oleh dua referensi berikut:
// Menukar dua referensi.
using System;
class TukarRef {
int
a, b;
public
TukarRef (int i, int j)
{
a = i;
b = j;
}
public
void Tampil() {
Console.WriteLine("a: {0}, b: {1}", a, b);
}
// Metode ini menukar argumennya.
public
void Tukar(ref TukarRef ob1, ref TukarRef ob2)
{
TukarRef t;
t = ob1;
ob1 = ob2;
ob2 = t;
}
}
class DemoTukarRef {
static
void Main() {
TukarRef x = new TukarRef(1, 2);
TukarRef y = new TukarRef(3, 4);
Console.Write("x sebelum pemanggilan: ");
x.Tampil();
Console.Write("y sebelum pemanggilan: ");
y.Tampil();
Console.WriteLine();
// Saling-tukar dua objek yang ditunjuk
oleh x dan y.
x.Tukar(ref x, ref y);
x.Tampil();
Console.Write("y setelah pemanggilan: ");
y.Tampil();
}
}
Keluaran dari
program ini adalah:
x sebelum pemanggilan: a: 1, b: 2
y sebelum pemanggilan: a: 3, b: 4
a: 3, b: 4
y setelah pemanggilan: a: 1, b: 2
Pada contoh ini,
metode Tukar() menukar objek yang
ditunjuk oleh kedua argumennya. Sebelum pemanggilan Tukar(), x menunjuk ke
sebuah objek yang memuat nilai 1 dan 2, dan y menunjuk ke sebuah objek yang memuat nilai 3 dan 4. Setelah
pemanggilan Tukar(), x menunjuk ke sebuah objek yang memuat
nilai 3 dan 4, dan y menunjuk ke
sebuah objek yang memuat nilai 1 dan 2. Jika parameter ref tidak digunakan, maka penukaran di dalam Tukar() tidak akan berpengaruh di luar Tukar(). Anda bisa membuktikan hal ini dengan menghapus ref dari Tukar().
Menggunakan
Jumlah Argumen Tak-Tentu
Ketika Anda
menciptakan sebuah metode, biasanya Anda telah mengetahui jumlah argumen yang
akan dilewatkan kepadanya, tetapi ada kalanya Anda tidak mengetahuinya.
Kadangkala Anda ingin menciptakan sebuah metode yang dapat dilewatkan sejumlah
argumen tak tentu banyaknya. Sebagai contoh, perhatikan sebuah metode yang
mencari nilai terkecil dari sebuah himpunan nilai. Kepada metode semacam itu
dapat dilewatkan dua nilai, atau tiga, atau empat, dan seterusnya. Metode
semacam itu tidak bisa diciptakan menggunakan parameter biasa. Anda harus
menggunakan sebuah parameter bertipe spesial, yaitu parameter params.
Pemodifikasi params dipakai untuk mendeklarasikan
sebuah parameter array yang akan menerima sejumlah nol atau lebih argumen.
Jumlah elemen di dalam array akan sama dengan jumlah argumen yang dilewatkan
kepada metode. Program Anda kemudian mengakses array tersebut untuk mendapatkan
argumen.
Berikut
merupakan sebuah contoh yang menggunakan params
dalam menciptakan sebuah metode bernama MinNil(),
yang menghasilkan nilai minimum dari sehimpunan nilai:
// Demonstrasi params.
using System;
class Min {
public
int MinNilai(params int[] angka) {
int m;
if (angka.Length == 0)
{
Console.WriteLine("Error: tidak ada argumen.");
return 0;
}
m =
angka[0];
for (int i = 1; i < angka.Length; i++)
if (angka[i] < m) m = angka[i];
return m;
}
}
class DemoParams {
static
void Main() {
Min ob = new Min();
int
min;
int
a = 10, b = 20;
// Memanggil dengan 2 nilai.
min = ob.MinNilai(a, b);
Console.WriteLine("Minimum adalah " + min);
// Memanggil dengan 3 nilai.
min = ob.MinNilai(a, b, -1);
Console.WriteLine("Minimum adalah " + min);
// Memanggil dengan 5 nilai.
min = ob.MinNilai(18, 23, 3, 14, 25);
Console.WriteLine("Minimum adalah " + min);
// Dapat memanggil dengan sebuah array
int.
int[]
args = { 45, 67, 34, 9, 112, 8 };
min = ob.MinNilai(args);
Console.WriteLine("Minimum adalah " + min);
}
}
Keluaran dari
program ini adalah:
Minimum adalah 10
Minimum adalah -1
Minimum adalah 3
Minimum adalah 8
Setiap kali MinNilai() dipanggil, argumen-argumen
dilewatkan kepadanya melalui array angka.
Panjang dari array sama dengan jumlah elemen. Jadi, Anda dapat menggunakan MinNilai() untuk mencari nilai minimum
dari sejumlah nilai.
Perhatikan
pemanggilan terakhir terhadap MinNilai(),
dimana sebuah array int dilewatkan
kepada metode tersebut. Hal ini legal untuk dilakukan. Ketika sebuah parameter
params diciptakan, ia menerima sejumlah argumen sebanyak tak-tentu atau sebuah
array yang memuat beberapa argumen.
Meskipun Anda
dapat melewatkan sebuah parameter params yang memuat sejumlah tak-tentu
argumen, semua argumen harus bertipe kompatibel. Sebagai contoh, pemanggilan MinNilai() seperti ini:
min = ob.MinNil(1, 2.2); // Salah!
tidak diijinkan
karena tidak ada konversi otomatis dari double(2.2)
menjadi int, yang merupakan tipe
dari array angka di dalam MinNilai().
Ketika
menggunakan params, Anda perlu
hati-hati mengenai kondisi batas karena sebuah parameter params dapat menerima
sejumlah tak-tentu argumen, bahkan nol!. Sebagai contoh, secara sintaks adalah
valid untuk memanggil MinNilai()
seperti ini:
min = ob.MinNilai(); // tidak ada argumen
min = ob.MinNilai(3); // 1 argumen
Inilah mengapa
ada pemeriksaan di dalam MinNilai()
untuk memastikan bahwa sedikitnya satu elemen harus ada di dalam array angka sebelum mencoba untuk mengakses
elemen tersebut. Jika tidak ada pemeriksaan, maka eksepsi runtime akan dihasilkan jika MinNilai()
dipanggil tanpa argumen. (Eksepsi akan dipelajari pada Bab 12). Pada program
berikut, metode TampilArg()
mengambil satu parameter string dan
sebuah array integer params:
// Menggunakan parameter
reguler dengan parameter params.
using System;
class KelasKu
{
public
void TampilArg(string msg, params int[] angka)
{
Console.Write(msg
+ ": ");
foreach
(int i in angka)
Console.Write(i + " ");
Console.WriteLine();
}
}
class DemoParams2 {
static
void Main() {
KelasKu ob = new KelasKu();
ob.TampilArg("Berikut adalah beberapa integer",
1, 2, 3, 4, 5);
ob.TampilArg("Berikut dua lagi integer",
17, 20);
}
}
Berikut adalah
keluaran program yang dihasilkan:
Berikut adalah beberapa integer: 1 2 3 4 5
Berikut dua lagi integer: 17 20
Menghasilkan
Nilai Balik Objek
Sebuah metode
dapat menghasilkan nilai balik bertipe sembarang, termasuk tipe kelas. Sebagai
contoh, versi berikut dari kelas PersegiPanjang
menyertakan sebuah metode bernama Perbesar()
yang menciptakan suatu persegipanjang yang secara proporsional sama dengan
persegipanjang pemanggil, tetapi lebih besar dengan sebuah faktor tertentu:
// Menghasilkan sebuah
objek.
using System;
class PersegiPanjang {
int
lebar;
int
tinggi;
public
PersegiPanjang(int w, int h)
{
lebar = w;
tinggi = h;
}
public
int Luas() {
return
lebar * tinggi;
}
public
void Tampil() {
Console.WriteLine(lebar
+ " " + tinggi);
}
/* Menghasilkan sebuah persegipanjang yang
lebih besar dengan
faktor tertentu daripada persegipanjang
pemanggil. */
public
PersegiPanjang Perbesar(int faktor)
{
return
new PersegiPanjang(lebar * faktor, tinggi * faktor);
}
}
class ObjekPersegiPanjang {
static
void Main() {
PersegiPanjang r1 = new PersegiPanjang(4, 5);
Console.Write("Dimensi dari r1: ");
r1.Tampil();
Console.WriteLine("Luas dari r1: " + r1.Luas());
Console.WriteLine();
// Menciptakan sebuah persegipanjang dua
kali lebih besar dari r1.
PersegiPanjang r2 = r1.Perbesar(2);
Console.Write("Dimensi dari r2: ");
r2.Tampil();
Console.WriteLine("Luas dari r2: " + r2.Luas());
}
}
Keluaran program
ditampilkan di sini:
Dimensi dari r1: 4 5
Luas dari r1: 20
Dimensi dari r2: 8 10
Luas dari r2: 80
Ketika sebuah
objek dijadikan nilai balik oleh metode, objek tersebut tetap ada sampai tidak
ada lagi referensi yang menunjuk kepadanya. Jadi, sebuah objek tidak akan
dihancurkan hanya karena metode yang menciptakannya berhenti.
Salah satu
terapan dari hal ini adalah kelas Factory.
Kelas Factory dipakai unutk
menciptakan objek dari kelasnya sendiri. Dalam beberapa situasi, Anda mungkin
tidak ingin memberikan akses terhadap konstruktor kelas kepada pengguna karena
alasan keamanan atau karena penciptaan objek bergantung pada beberapa faktor
luar. Berikut diberikan contohnya:
// Menggunakan kelas
Factory.
using System;
class KelasKu {
int
a, b; // private
// Menciptakan kelas Factory untuk KelasKu.
public
KelasKu Factory(int i, int j)
{
KelasKu t = new KelasKu();
t.a = i;
t.b = j;
return
t; // menghasilkan objek
}
public
void Tampil() {
Console.WriteLine("a dan b: " + a + " " +
b);
}
}
class MembuatObjek {
static
void Main() {
KelasKu ob = new KelasKu();
int
i, j;
// Menghasilkan objek-objek menggunakan
Factory.
for(i=0,
j=10; i < 10; i++, j--) {
KelasKu obLain = ob.Factory(i, j); //
membuat objek
obLain.Tampil();
}
Console.WriteLine();
}
}
Program tersebut
menghasilkan keluaran:
a dan b: 0 10
a dan b: 1 9
a dan b: 2 8
a dan b: 3 7
a dan b: 4 6
a dan b: 5 5
a dan b: 6 4
a dan b: 7 3
a dan b: 8 2
a dan b: 9 1
KelasKu tidak mendefinisikan konstruktor, jadi hanya konstruktor
default yang disediakan oleh kompiler. Oleh karena itu, tidak dimungkinkan
untuk menetapkan nilai untuk a dan b menggunakan konstruktor. Tetapi,
kelas Factory dapat menciptakan
objek dimana di dalamnya a dan b diberikan nilai. Di samping itu,
karena a dan b privat, penggunaan Factory()
merupakan satu-satunya cara untuk menetapkan kedua nilai dari variabel
tersebut.
Di dalam Main(), sebuah objek KelasKu diinstansiasi, dan metode Factory pada objek tersebut dipakai di
dalam loop for untuk menciptakan
sepuluh objek. Baris kode yang menciptakan sepuluh objek ditampilkan di sini:
for(i=0, j=10; i < 10; i++,
j--) {
KelasKu obLain = ob.Factory(i, j); //
membuat objek
obLain.Tampil();
}
Pada tiap
iterasi, sebuah referensi objek bernama obLain
diciptakan, dan ia ditugasi dengan referensi ke objek yang diciptakan oleh Factory. Di akhir tiap iterasi di dalam
loop, obLain menjadi keluar skop, sehingga objek tersebut akan dihancurkan.
Menghasilkan
Nilai Balik Array
Karena di dalam
C# array diimplementasikan sebagai objek, sebuah metode juga bisa menjadikan
array sebagai nilai balik. Sebagai contoh, pada program berikut, metode CariFaktor() menghasilkan nilai balik
sebuah array yang memuat beberapa faktor dari argumen yang dilewatkan
kepadanya:
// Menghasilkan sebuah
array.
using System;
class Faktor {
/* Menghasilkan sebuah array yang memuat
faktor-faktor dari angka.
jumfaktor akan memuat jumlah faktor yang
ditemukan. */
public
int[] CariFaktor(int angka, out int jumfaktor) {
int[]
faktor = new int[80]; // berukuran
80
int
i, j;
// Mencari faktor-faktor dan menempatkannya
di dalam array faktor.
for
(i = 2, j = 0; i < angka / 2 + 1; i++)
if
((angka % i) == 0)
{
faktor[j] = i;
j++;
}
jumfaktor = j;
return
faktor;
}
}
class CariFaktor {
static
void Main() {
Faktor f = new Faktor();
int
jumfaktor;
int[]
faktor;
faktor = f.CariFaktor(1000, out
jumfaktor);
Console.WriteLine("Faktor dari 1000 adalah: ");
for
(int i = 0; i < jumfaktor; i++)
Console.Write(faktor[i] + " ");
Console.WriteLine();
}
}
Keluaran program
ditampilkan di sini:
Faktor dari 1000 adalah:
2 4 5 8 10 20 25 40 50 100 125 200 250 500
Di dalam Faktor, CariFaktor() dideklarasikan seperti ini:
public
int[] CariFaktor(int angka, out int jumfaktor) {
Perhatikan
bagaimana tipe nilai balik array int dispesifikasi. Sintaks ini dapat
digeneralisir. Sebagai contoh, berikut mendeklarasikan sebuah metode bernama suatuMetode() yang menghasilkan nilai
balik array double dua dimensi:
public double[,] suatuMetode() { // ...
Mengoverload
Metode
Dalam C#, dua
atau lebih metode di dalam kelas yang sama dapat mempunyai nama sama, sepanjang
deklarasi parameternya berbeda. Ketika hal ini terjadi, metode-metode tersebut
dikatakan dioverload, dan proses ini dikenal dengan pengoverloadan metode.
Pengoverloadan metode merupakan salah satu cara C# mengimplementasikan
polimorfisme.
Secara umum,
untuk mengoverload sebuah metode, cukup dengan mendeklarasikan beberapa versi
metode yang berbeda. Kompiler akan menangani semuanya. Anda harus
mengantisipasi satu hal penting ini: Tipe dan/atau jumlah parameter dari tiap
metode teroverload harus berbeda. Tidak cukup bagi metode-metode teroverload
untuk berbeda pada tipe nilai baliknya saja. Semua versi teroverload harus
berbeda dalam tipe parameter atau/dan jumlah parameternya.
Ketika sebuah
metode parameter teroverload dipanggil, versi dari metode yang dieksekusi
adalah yang memiliki parameter cocok dengan argumen. Berikut adalah contoh
sederhana yang mengilustrasikan pengoverloadan metode:
// Demonstrasi pengoverloadan metode.
using System;
class Overload
{
public void DemoOvl()
{
Console.WriteLine("Tidak ada
parameter");
}
// Mengoverload
DemoOvl untuk satu parameter.
public void DemoOvl(int a)
{
Console.WriteLine("Satu parameter: " + a);
}
// Mengoverload
DemoOvl untuk dua parameter.
public int DemoOvl(int a, int b)
{
Console.WriteLine("Dua parameter: " + a + "
" + b);
return a + b;
}
// Mengoverload
DemoOvl untuk dua parameter double.
public double DemoOvl(double a, double b)
{
Console.WriteLine("Dua parameter double: " +
a + " " + b);
return a + b;
}
}
class DemoOverload {
static void Main() {
Overload ob = new Overload();
int resI;
double resD;
// Memanggil
semua versi dari DemoOvl().
ob.DemoOvl();
Console.WriteLine();
ob.DemoOvl(2);
Console.WriteLine();
resI =
ob.DemoOvl(4, 6);
Console.WriteLine("Hasil dari ob.DemoOvl(4, 6): " +
resI);
Console.WriteLine();
resD = ob.DemoOvl(1.1,
2.32);
Console.WriteLine("Hasil dari ob.DemoOvl(1.1, 2.32):
" + resD);
}
}
Program
menghasilkan keluaran berikut:
Tidak ada parameter
Satu parameter: 2
Dua parameter: 4 6
Hasil dari ob.DemoOvl(4, 6): 10
Dua parameter double: 1.1 2.32
Hasil dari ob.DemoOvl(1.1, 2.32): 3.42
Seperti yang
dapat Anda lihat, DemoOvl()
dioverload empat kali. Versi pertama tidak mengambil parameter apapun; kedua
mengambil satu parameter integer; ketiga mengambil dua parameter integer; dan
keempat mengambil dua parameter double.
Perhatikan bahwa dua versi pertama dari DemoOvl()
menghasilkan void dan dua versi
kedua menghasilkan nilai balik. Jadi, mencoba untuk menggunakan dua versi DemoOvl() berikut akan menyebabkan
error:
// DemoOvl(int) OK!.
public
void DemoOvl(int
a) {
Console.WriteLine("Satu
parameter: " + a);
}
/* Error! Dua DemoOvl(int) tidak OK, meskipun
berbeda tipe
nilai balik. */
public
int DemoOvl(int a) {
Console.WriteLine("Satu parameter:
" + a);
return a * a;
}
Seperti Anda
ingat dari Bab 2, C# menyediakan konversi tipe implisit otomatis. Konversi ini
juga diterapkan pada parameter dari metode teroverload. Sebagai contoh,
perhatikan berikut:
// Konversi tipe implisit
dapat mempengaruhi metode teroverload.
using System;
class Overload2 {
public
void MetodeKu(int x) {
Console.WriteLine("Di dalam MetodeKu(int): " + x);
}
public
void MetodeKu(double x)
{
Console.WriteLine("Di dalam MetodeKu(double): " + x);
}
}
class KonvTipe {
static
void Main()
{
Overload2 ob = new Overload2();
int
i = 10;
double
d = 10.1;
byte
b = 99;
short
s = 10;
float
f = 11.5F;
ob.MetodeKu(i); // memanggil
ob.MetodeKu(int)
ob.MetodeKu(d); // memanggil
ob.MetodeKu(double)
ob.MetodeKu(b); // memanggil
ob.MetodeKu(int) -- konversi tipe
ob.MetodeKu(s); // memanggil
ob.MetodeKu(int) -- konversi tipe
ob.MetodeKu(f); // memanggil
ob.MetodeKu(double) -- konversi tipe
}
}
Program tersebut
menghasilkan keluaran:
Di dalam MetodeKu(int): 10
Di dalam MetodeKu(double): 10.1
Di dalam MetodeKu(int): 99
Di dalam MetodeKu(int): 10
Di dalam MetodeKu(double): 11.5
Pada contoh ini,
hanya dua versi MetodeKu() yang didefinisikan:
satu yang memiliki sebuah parameter int
dan lainnya yang mempunyai sebuah parameter double. Namun, dimungkinkan untuk melewatkan sebuah nilai byte, short, atau float kepada
MetodeKu(). Pada kasus byte dan short, karena C# secara otomatis akan mengkonversinya
menjadi int. Jadi, MetodeKu(int) yang dipanggil pada kasus
tersebut. Pada kasus float, nilai
dikonversi menjadi double dan MetodeKu(double) yang dipanggil.
Adalah hal
penting untuk memahami bahwa konversi implisit hanya bisa diterapkan jika ada
kesesuaian tipe antara parameter dan argumen. Sebagai contoh, berikut adalah
program sebelumnya dengan penambahan sebuah versi MetodeKu() yang menspesifikasi parameter byte:
// Konversi tipe implisit
dapat mempengaruhi metode teroverload.
using System;
class Overload2 {
public
void MetodeKu(byte x) {
Console.WriteLine("Di dalam MetodeKu(byte): " + x);
}
public
void MetodeKu(int x) {
Console.WriteLine("Di dalam MetodeKu(int): " + x);
}
public
void MetodeKu(double x)
{
Console.WriteLine("Di dalam MetodeKu(double): " + x);
}
}
class KonvTipe {
static
void Main()
{
Overload2 ob = new Overload2();
int
i = 10;
double
d = 10.1;
byte
b = 99;
short
s = 10;
float
f = 11.5F;
ob.MetodeKu(i); // memanggil
ob.MetodeKu(int)
ob.MetodeKu(d); // memanggil
ob.MetodeKu(double)
ob.MetodeKu(b); // memanggil
ob.MetodeKu(int) -- konversi tipe
ob.MetodeKu(s); // memanggil
ob.MetodeKu(int) -- konversi tipe
ob.MetodeKu(f); // memanggil
ob.MetodeKu(double) -- konversi tipe
}
}
Keluaran program
ditampilkan di sini:
Di dalam MetodeKu(int): 10
Di dalam MetodeKu(double): 10.1
Di dalam MetodeKu(byte): 99
Di dalam MetodeKu(int): 10
Di dalam MetodeKu(double): 11.5
Pada versi ini,
karena terdapat sebuah versi dari MetodeKu()
yang mengambil sebuah argumen byte,
ketika MetodeKu() dipanggil dengan
sebuah argmen byte, MetodeKu(byte) dipanggil dan konversi
otomatis menjadi int tidak terjadi.
Kedua ref dan out dapat dilibatkan dalam overload. Sebagai contoh, berikut
didefinisikan dua metode berbeda:
public
void MetodeKu(int
x) {
Console.WriteLine("Di dalam MetodeKu(int): " + x);
}
public
void MetodeKu(ref
int x) {
Console.WriteLine("Di dalam MetodeKu(ref int): " +
x);
}
Jadi,
ob.MetodeKu(i)
memanggil MetodeKu(int x), tetapi
ob.MetodeKu(ref
i)
memanggil MetodeKu(ref int x).
Meskipun ref dan out dapat dipakai dalam overload, perbedaan keduanya tidak cukup.
Sebagai contoh, dua versi MetodeKu()
berikut adalah tak valid:
// Salah!
public
void MetodeKu(out int
x) { // ...
public
void MetodeKu (ref int
x) { // ...
Pada kasus ini,
kompiler tidak bisa membedakan antara kedua versi MetodeKu() hanya karena yang satu menggunakan parameter out int dan yang lain menggunakan
parameter ref int.
Mengoverload
Konstruktor
Seperti metode,
konstruktor juga dapat dioverload. Dengan melakukannya, Anda dapat menciptakan
objek dalam berbagai cara. Sebagai contoh, perhatikan program berikut:
// Demonstrasi konstruktor
teroverload.
using System;
class KelasKu
{
public
int x;
public
KelasKu()
{
Console.WriteLine("Di dalam KelasKu().");
x = 0;
}
public
KelasKu(int i)
{
Console.WriteLine("Di
dalam KelasKu(int).");
x = i;
}
public
KelasKu(double d)
{
Console.WriteLine("Di dalam KelasKu(double).");
x = (int)d;
}
public
KelasKu(int i, int j)
{
Console.WriteLine("Di dalam KelasKu(int, int).");
x = i * j;
}
}
class DemonOverKonst
{
static
void Main()
{
KelasKu t1 = new KelasKu();
KelasKu t2 = new KelasKu(88);
KelasKu t3 = new KelasKu(17.23);
KelasKu t4 = new KelasKu(2, 4);
Console.WriteLine("t1.x: " + t1.x);
Console.WriteLine("t2.x: " + t2.x);
Console.WriteLine("t3.x: " + t3.x);
Console.WriteLine("t4.x: " + t4.x);
}
}
Keluaran program
ditampilkan di sini:
Di dalam KelasKu().
Di dalam KelasKu(int).
Di dalam KelasKu(double).
Di dalam KelasKu(int, int).
t1.x: 0
t2.x: 88
t3.x: 17
t4.x: 8
KelasKu() dioverload dengan empat cara,
masing-masing menciptakan objek secara berbeda. Konstruktor yang sesuai
dipanggil berdasarkan argumen yang dispesifikasi ketika new dieksekusi. Dengan mengoverload konstruktor suatu kelas, Anda
memberikan fleksibilitas pada pengguna kelas dalam hal penciptaan objek.
Salah satu
alasan yang paling umum dijumpai mengapa konstruktor perlu dioverload adalah
untuk membolehkan satu objek menginisialisasi objek lainnya. Sebagai contoh,
berikut merupakan versi mutakhir dari kelas Tumpukan yang sebelumnya telah ditulis. Versi ini mengijinkan satu
tumpukan dikonstruksi dari tumpukan lain:
// Kelas Tumpukan untuk karakter.
using System;
class Tumpukan
{
// Anggota-anggota
ini privat.
char[] tumpukan; // menampung tumpukan
int iat; // indeks dari atas tumpukan
// Menciptakan
sebuah Tumpukan kosong dengan ukuran tertentu.
public Tumpukan(int ukuran)
{
tumpukan = new char[ukuran]; // mengalokasikan memori untuk tumpukan
iat = 0;
}
// Mengkonstruksi
sebuah Tumpukan dari sebuah tumpukan.
public Tumpukan(Tumpukan ob) {
// Mengalokasikan
memori untuk tumpukan.
tumpukan = new char[ob.tumpukan.Length];
// Menyalin
elemen-elemen ke tumpukan baru.
for(int i=0; i < ob.iat; i++)
tumpukan[i] =
ob.tumpukan[i];
// Menetapkan iat
untuk tumpukan baru.
iat = ob.iat;
}
// Mendorong
karakter ke atas tumpukan.
public void Push(char ch)
{
if (iat == tumpukan.Length)
{
Console.WriteLine(" -- Tumpukan penuh.");
return;
}
tumpukan[iat] =
ch;
iat++;
}
// Menghapus sebuah
karakter dari atas tumpukan.
public char Pop()
{
if (iat == 0)
{
Console.WriteLine(" -- Tumpukan kosong.");
return (char)0;
}
iat--;
return tumpukan[iat];
}
// Menghasilkan
true jika tumpukan penuh.
public bool apaPenuh()
{
return iat == tumpukan.Length;
}
// Menghasilkan
true jika tumpukan kosong.
public bool apaKosong()
{
return iat == 0;
}
// Menghasilkan
kapasitas total dari tumpukan.
public int Kapasitas()
{
return tumpukan.Length;
}
// Menghasilkan jumlah
objek yang saat ini pada tumpukan.
public int GetJumlah()
{
return iat;
}
}
// Demonstrasi kelas Tumpukan.
class DemoTumpukan {
static void Main() {
Tumpukan tumpukan1
= new Tumpukan(10);
char ch;
int i;
// Menempatkan
beberapa karakter pada tumpukan1.
Console.WriteLine("Menempatkan A sampai J pada tumpukan1.");
for (i = 0; !tumpukan1.apaPenuh(); i++)
tumpukan1.Push((char)('A' +
i));
// Menciptakan
sebuah salinan dari tumpukan1.
Tumpukan tumpukan2 = new Tumpukan(tumpukan1);
// Menampilkan isi
dari tumpukan1.
Console.Write("Isi dari tumpukan1: ");
while (!tumpukan1.apaKosong()) {
ch =
tumpukan1.Pop();
Console.Write(ch);
}
Console.WriteLine();
Console.Write("Isi dari tumpukan2: ");
while (!tumpukan2.apaKosong())
{
ch =
tumpukan2.Pop();
Console.Write(ch);
}
Console.WriteLine("\n");
}
}
Keluaran program
ditampilkan di sini:
Menempatkan A sampai J pada tumpukan1.
Isi dari tumpukan1: JIHGFEDCBA
Isi dari tumpukan2: JIHGFEDCBA
Dalam DemoTumpukan, tumpukan pertama, tumpukan1, dikonstruksi dan diisi
dengan beberapa karakter. Tumpukan ini kemudian dipakai untuk mengkonstruksi
tumpukan kedua, tumpukan2. Ini menyebabkan
konstruktor Tumpukan berikut
dieksekusi:
// Mengkonstruksi sebuah
Tumpukan dari sebuah tumpukan.
public Tumpukan(Tumpukan
ob) {
// Mengalokasikan memori untuk tumpukan.
tumpukan = new char[ob.tumpukan.Length];
// Menyalin elemen-elemen ke tumpukan baru.
for(int i=0; i < ob.iat; i++)
tumpukan[i] = ob.tumpukan[i];
// Menetapkan iat untuk tumpukan baru.
iat = ob.iat;
}
Di dalam
konstruktor ini, sebuah array dialokasikan, cukup panjang untuk menampung semua
elemen yang dimuat tumpukan yang dilewatkan di dalam ob. Kemudian, isi array di dalam ob disalin ke array baru. Setelah konstruktor selesai dieksekusi,
tumpukan baru dan tumpukan asli tetap terpisah, tetapi dengan isi sama.
Memanggil
Konstruktor Teroverload Melalui this
Ketika bekerja
dengan konstruktor teroverload, dimungkinkan dalam menggunakan satu konstruktor
memanggil konstruktor lain. Dalam C#, ini dilakukan dengan menggunakan
katakunci this. Bentuk umumnya
ditunjukkan di sini:
nama-konstruktor(daftar-parameter1)
: this(daftar-parameter2) {
// ... tubuh
konstruktor, boleh kosong
}
Ketika
konstruktor dieksekusi, konstruktor teroverload yang cocok dengan daftar
parameter yang dispesifikasi oleh daftar-parameter2 lebih dahulu dieksekusi. Kemudian,
jika terdapat sembarang statemen di dalam konstruktor asli, semuanya akan
dieksekusi. Berikut disajikan sebuah contoh:
// Demonstrasi pemanggilan
sebuah konstruktor melalui this.
using System;
class XYCoord {
public
int x, y;
public
XYCoord() : this(0, 0) {
Console.WriteLine("Di dalam XYCoord()");
}
public
XYCoord(XYCoord obj) : this(obj.x,
obj.y) {
Console.WriteLine("Di dalam XYCoord(obj)");
}
public
XYCoord(int i, int j) {
Console.WriteLine("Di dalam XYCoord(int, int)");
x = i;
y = j;
}
}
class DemonOverloadKonst {
static
void Main() {
XYCoord t1 = new XYCoord();
XYCoord t2 = new XYCoord(8, 9);
XYCoord t3 = new XYCoord(t2);
Console.WriteLine("t1.x, t1.y: " + t1.x + ",
" + t1.y);
Console.WriteLine("t2.x, t2.y: " + t2.x + ",
" + t2.y);
Console.WriteLine("t3.x, t3.y: " + t3.x + ",
" + t3.y);
}
}
Program menghasilkan
keluaran berikut:
Di dalam XYCoord(int, int)
Di dalam XYCoord()
Di dalam XYCoord(int, int)
Di dalam XYCoord(int, int)
Di dalam XYCoord(obj)
t1.x, t1.y: 0, 0
t2.x, t2.y: 8, 9
t3.x, t3.y: 8, 9
Berikut adalah
bagaimana program bekerja. Di dalam kelas XYCoord,
satu-satunya konstruktor yang secara aktual menginisialisasi bidang x dan y adalah XYCoord(int, int).
Dua konstruktor yang lain hanya memanggil XYCoord(int,
int) melalui this. Sebagai
contoh, ketika objek t1 diciptakan,
konstruktorya, XYCoord(int, int),
dipanggil. ini mengakibatkan this(0, 0)
dieksekusi, dimana pada kasus ini diterjemahkan menjadi pemanggilan terhadap XYCoord(0, 0). Penciptaan objek t2 sama seperti penciptaan t1.
Keuntungan lain
adalah bahwa Anda dapat menciptakan konstruktor dengan argumen default
terimplikasi yang digunakan ketika argumen tidak dispesifikasi secara
eksplisit. Sebagai contoh, Anda dapat menciptakan konstruktor XYCoord lain seperti ini:
public XYCoord(int x) : this(x, x) { }
Konstruktor ini
secara otomatis menetapkan koordinat y
bernilai sama dengan koordinat x.
Penginisialisasi
Objek
Penginisialisasi
objek menyediakan cara lain dalam menciptakan sebuah objek dan menginisialisasi
bidang dan propertinya. (Lihat Bab 9 untuk diskusi tentang properti). Dengan
menggunakan penginisialisasi objek, Anda tidak memanggil konstruktor kelas
dengan cara yang normal. Tetapi, Anda perlu menspesifikasi nama bidang dan/atau
properti yang akan diinisialisasi, memberikannya masing-masing dengan nilai
awal. Jadi, sintaks penginisialisasi objek memberikan cara alternatif untuk
secara eksplisit memanggil konstruktor kelas. Akan diberikan sebuah contoh
sederhana:
// Contoh demonstrasi
sederhana yang menggunakan penginisialisasi objek.
using System;
class KelasKu {
public
int Hitung;
public
string Str;
}
class DemoInitObj {
static
void Main() {
// Menciptakan objek KelasKu
menggunakan penginisialisasi objek.
KelasKu obj = new KelasKu { Hitung = 100, Str = "Pengujian" };
Console.WriteLine(obj.Hitung
+ " " + obj.Str);
}
}
Berikut adalah
keluaran program:
100 Pengujian
Seperti
ditampilkan pada keluaran, nilai dari obj.Hitung
diinisialisasi dengan 100 dan nilai dari obj.Str
diinisialisasi dengan “Pengujian”.
Perhatikan bahwa KelasKu tidak
mendefinisikan sembarang konstruktor eksplisit, dan bahwa sintaks konstruktor
normal tidak digunakan. Objek obj diciptakan menggunakan baris berikut:
KelasKu obj = new KelasKu {
Hitung = 100, Str = "Pengujian"
};
Di sini, nama
bidang secara eksplisit dispesifikasi bersamaan dengan nilai awalnya.
Penting untuk
memahami bahwa urutan penginisialisasi adalah hal yang tidak penting. Sebagai
contoh, obj dapat diinisialisasi
seperti ini:
KelasKu obj = new
KelasKu { Str = "Pengujian", Hitung = 100 };
Dalam statemen
ini, inisialisasi atas Str
mendahului inisialisasi atas Hitung.
Di dalam program, hal itu kebalikannya. Namun, tetapi akan memberikan hasil
sama.
Berikut
merupakan format umum atas sintaks inisialisasi objek:
new nama-kelas
{ nama = ekspr, nama = ekspr, nama = ekspr,
... }
Di sini, nama menspesifikasi
nama bidang atau properti yang diakses melalui nama-kelas. Tipe ekpresi
penginisialisasi yang ditentukan oleh ekspr harus kompatibel dengan tipe bidang
atau properti.
Argumen
Opsional
C# mempunyai
fitur baru yang menambah fleksibilitas dalam hal penspesifikasian argumen
ketika sebuah metode dipanggil. Fitur ini dinamakan argumen opsional, yang
membolehkan Anda mendefinisikan sebuah nilai default untuk suatu parameter
metode. Nilai default ini dipakai jika argumen yang berkaitan dengan parameter
tersebut tidak dispesifikasi ketika metode dipanggil. Argumen opsional dapat
menyederhanakan pemanggilan metode, dimana di dalamnya argumen default dapat
diterapkan pada beberapa parameter.
Argumen opsional
diciptakan dengan membuat parameter opsional. Untuk melakukannya, Anda hanya
perlu menspesifikasi nilai default untuk parameter, menggunakan sintaks yang
sama dengan inisialisasi variabel. Nilai default harus berupa sebuah ekspresi
konstanta. Sebagai contoh, perhatikan deklarasi metode ini:
static
void MetOpsArg(int
alfa, int beta=10, int gamma = 20) {
Di sini terdapat
dua parameter opsional yang dideklarasikan, beta dan gamma. Pada
kasus ini, beta mempunyai nilai
default 10, dan gamma mempunyai
nilai default 20. Kedua nilai default ini digunakan jika tidak ada argumen yang
dispesifikasi untuk parameter tersebut ketika metode dipanggil. Perhatikan
bahwa alfa bukan parameter opsional,
tetapi parameter normal, jadi argumen untuknya selalu diperlukan. Metode MetOpsArg() dapat dipanggil dengan
beberapa cara ini:
//
Melewatkan semua argumen secara eksplisit.
MetOpsArg (1, 2, 3);
//
Membiarkan gamma menjadi default.
MetOpsArg (1, 2);
//
Membiarkan kedua beta dan gamma menjadi default.
MetOpsArg (1);
Pemanggilan
pertama melewatkan nilai 1 kepada alfa,
2 kepada beta, dan 3 kepada gamma. Jadi, ketiga argumen
dispesifikasi secara eksplisit, dan tidak ada nilai default yang digunakan.
Pemanggilan kedua melewatkan nilai 1 kepada alfa dan 2 kepada beta,
tetapi gamma menggunakan nilai
default 20. Pemanggilan ketiga melewatkan 1 kepada alfa, dan membiarkan beta
dan gamma menggunakan nilai default.
Adalah hal penting untuk memahami bahwa tidak bisa membuat beta menjadi nilai default tanpa juga menetapkan gamma menjadi default. Begitu argumen
pertama menjadi default, maka argumen lainnya harus juga default.
Program berikut
menunjukkan keseluruhan proses yang baru saja dijelaskan:
// Demonstrasi argumen
opsional.
using System;
class DemoOpsiArg {
static
void MetOpsArg(int alfa, int beta = 10, int gamma = 20) {
Console.WriteLine("Berikut adalah alfa, beta, dan gamma: "
+
alfa + " " +
beta + " " + gamma);
}
static
void Main() {
// Melewatkan semua argumen secara
eksplisit.
MetOpsArg(1, 2, 3);
// Membuat gamma default.
MetOpsArg(1, 2);
//
Membuat kedua beta dan gamma default.
MetOpsArg(1);
}
}
Keluaran program
di sini menegaskan penggunaan argumen default:
Berikut
adalah alfa, beta, dan gamma: 1 2 3
Berikut
adalah alfa, beta, dan gamma: 1 2 20
Berikut adalah
alfa, beta, dan gamma: 1 10 20
Penting untuk
memahami bahwa semua parameter opsional harus berada di sisi kanan dari
parameter normal. Sebagai contoh, deklarasi berikut adalah salah:
int Contoh(string nama =
"user", int userId) { //
Error!
Untuk
membenarkan deklarasi ini, Anda harus mendeklarasikan userId sebelum nama.
Begitu Anda mulai mendeklarasikan parameter opsional, Anda tidak bisa lagi
menspesifikasi parameter normal. Sebagai contoh, deklarasi ini juga tidak
benar:
int Contoh(int akunId, string nama = "user", int userId) { // Error!
Karena nama adalah opsional, userId juga harus ditempatkan sebelum nama (atau userId juga dibuat menjadi
opsional).
Argumen
Opsional Versus Overloading
Dalam beberapa
kasus, argumen opsional bisa menjadi alternatif bagi pengoverloadan metode.
Untuk memahami mengapa, sekali lagi perhatikan MetOpsArg() yang tadi. Sebelum penggunaan argumen opsional dalam
C#, Anda akan memerlukan tiga versi berbeda dari MetOpsArg() untuk memiliki fungsionalitas yang sama. Ketiga versi
tersebut mempunyai deklarasi berikut:
static void MetOpsArg (int
alfa)
static void MetOpsArg (int
alfa, int beta)
static void MetOpsArg (int alfa, int beta, int gamma)
Ketiga metode
teroverload tersebut dapat dipanggil dengan satu, dua, atau tiga argumen. Tubuh
ketiga metode tersebut juga harus menyediakan nilai-nilai dari beta dan gamma ketika tidak dilewatkan. Pada kasus ini, argumen opsional
menjadi pendekatan yang lebih baik.
Argumen
Opsional Versus Ambiguitas
Satu masalah yang
bisa terjadi ketika menggunakan argumen opsional adalah ambiguitas. Ini terjadi
ketika sebuah metode yang mempunyai parameter opsional dioverload. Pada
beberapa kasus, kompiler menjadi tidak bisa menentukan versi mana yang akan
dipanggil ketika argumen opsional tidak dispesifikasi. Sebagai contoh,
perhatikan kedua versi dari MetOpsArg():
static
void MetOpsArg(int
alfa, int beta=10, int gamma = 20) {
Console.WriteLine("Berikut adalah
alfa, beta, dan gamma: " +
alfa + " " + beta + " " + gamma);
}
static void MetOpsArg (int alfa, double
beta=10.0, double gamma = 20.0) {
Console.WriteLine("Berikut adalah
alfa, beta, dan gamma: " +
alfa + " " + beta + " " + gamma);
}
Perhatikan bahwa
satu-satunya perbedaan antara kedua versi adalah tipe beta dan gamma, yang
merupakan parameter opsional. Dalam versi pertama, tipenya adalah int. Dalam versi kedua, tipenya adalah double. Pemanggilan terhadap MetOpsArg() berikut menjadi ambigu:
MetOpsArg(1); // Error! Ambigu
Pemanggilan ini
ambigu karena kompiler tidak mengetahui versi mana yang akan dipanggil. Pada
kasus ini, Andalah yang bertanggung jawab untuk menghindarinya.
Contoh
Praktis Dari Argumen Opsional
Program berikut
mengilustrasikan bagaimana argumen opsional dapat menyederhanakan pemanggilan
metode. Program ini mendeklarasikan sebuah metode bernama Tampil(), yang menampilkan sebuah string. String dapat ditampilkan
secara keseluruhan atau hanya sepotong saja.
// Menggunakan argumen
opsional untuk menyederhanakan pemanggilan metode.
using System;
class UseOptArgs {
// Menampilkan sepotong string.
static
void Tampil(string str, int mulai = 0, int berhenti = -1) {
if(berhenti
< 0)
berhenti = str.Length;
// Memeriksa kondisi di-luar-batas.
if(berhenti
> str.Length | mulai > berhenti | mulai < 0)
return;
for(int i=mulai; i < berhenti; i++)
Console.Write(str[i]);
Console.WriteLine();
}
static void Main() {
Tampil("ini adalah sebuah test");
Tampil("ini adalah sebuah test", 11);
Tampil("ini adalah sebuah test", 5, 12);
}
}
Keluaran program
ditampilkan di sini:
ini adalah sebuah test
sebuah test
dalah s
Akan diperiksa
metode Tampil() dengan lebih detil.
String yang akan ditampilkan dilewatkan di dalam argumen pertama. Argumen ini
diperlukan. Argumen kedua dan ketiga adalah opsional. Argumen opsional
menspesifikasi indeks awal dan indeks akhir dari sepenggal string untuk
ditampilkan. Jika sebuah nilai tidak dilewatkan kepada berhenti, maka nilai defaultnya adalah -1, yang mengindikasikan
titik berhenti adalah akhir string. Jika sebuah nilai tidak dilewatkan kepada mulai, maka nilai defaultnya adalah 0.
Oleh karena itu, jika tidak ada argumen opsional yang diberikan, string akan
ditampilkan secara menyeluruh. Ini berarti bahwa jika Anda memanggil Tampil() dengan satu argumen (string
yang akan ditampilkan), maka string ditampilkan secara keseluruhan. Jika Anda
memanggil Tampil() dengan dua
argumen, maka karakter yang dimulai dari mulai
sampai akhir karakter akan ditampilkan. Jika ketiga argumen dilewatkan, maka
sepenggal string dari mulai sampai berhenti akan ditampilkan.
Argumen
Bernama
Fitur lain yang
berkaitan dengan pelewatan argumen kepada sebuah metode adalah argumen bernama.
Argumen bernama ditambahkan oleh C# 4.0. Seperti Anda ketahui, ketika Anda
melewatkan argumen kepada sebuah metode, urutan argumen harus sesusi dengan urutan
parameter yang didefinisikan oleh metode. Dengan kata lain, posisi argumen di
dalam daftar argumen menentukan ke parameter mana ia ditugaskan. Argumen
bernama menghapus pembatasan ini. Dengan argumen bernama, Anda dapat
menspesifikasi nama parameter yang berkaitan dengan argumen tertentu. Dengan
menggunakan argumen bernama, urutan argumen menjadi tidak penting. Jadi,
argumen bernama sedikit seperti penginisialisasi objek yang dideskripsikan
sebelumnya, meskipun dengan sintaks yang berbeda.
Untuk menspesifikasi
sebuah argumen bernama, digunakan sintaks ini:
nama-param : nilai
Di sini, nama-param menspesifikasi
nama parameter yang akan menerima nilai yang dilewatkan. Berikut adalah contoh
sederhana yang mendemonstrasikan argumen bernama. Program menciptakan sebuah
metode bernama ApaFaktor() yang
menghasilkan true jika parameter
pertama dapat dibagi oleh parameter kedua (tanpa sisa).
// Menggunakan argumen
bernama.
using System;
class DemoArgBernama {
// Menentukan jika satu nilai bisa dibagi
oleh nilai lain (tanpa sisa).
static
bool ApaFaktor(int nil, int pembagi) {
if((nil
% pembagi) == 0) return true;
return
false;
}
static
void Main()
{
// Berikut menunjukkan berbagai cara dalam
memanggil ApaFaktor().
// Memanggil dengan menggunakan argumen
posisional.
if(ApaFaktor(10,
2))
Console.WriteLine("2 adalah faktor dari 10.");
// Memanggil dengan menggunakan argumen
bernama.
if(ApaFaktor(nil:
10, pembagi: 2))
Console.WriteLine("2 adalah faktor dari 10.");
// Urutan menjadi tidak penting dengan
argumen bernama.
if(ApaFaktor(pembagi:
2, nil: 10))
Console.WriteLine("2 adalah faktor dari 10.");
// Menggunakan argumen posisional dan argumen
bernama.
if(ApaFaktor(10,
pembagi: 2))
Console.WriteLine("2 adalah faktor dari 10.");
}
}
Keluaran program
ditampilkan di sini:
2 adalah faktor dari 10.
2 adalah faktor dari 10.
2 adalah faktor dari 10.
2 adalah faktor dari 10.
Seperti
ditampilkan pada keluaran, setiap pemanggilan ApaFaktor() menghasilkan hasil yang sama. Program mendemonstrasikan
dua aspek penting dari argumen bernama. Pertama, urutan penspesifikasian
argumen tidak menjadi masalah lagi. Sebagai contoh, kedua pemanggilan ini
adalah ekivalen:
ApaFaktor(nil :10, pembagi: 2)
ApaFaktor(pembagi: 2, nil: 10)
Menjadi
independen merupakan keuntungan utama dari argumen bernama. Ini berarti bahwa
Anda tidak perlu mengingat (atau bahkan mengetahui) urutan parameter di dalam
metode yang dipanggil. Kedua, perhatikan bahwa Anda dapat menspesifikasi sebuah
argumen posisional dan sebuah argumen bernama di dalam pemanggilan yang sama,
seperti:
ApaFaktor(10, pembagi: 2)
Akan tetapi,
perlu hati-hati, ketika menggabungkan argumen posisional dan argumen bernama,
semua argumen posisional harus ditempatkan sebelum argumen bernama.
Argumen bernama
juga dapat dipakai bersamaan dengan argumen opsional. Sebagai contoh,
diasumsikan bahwa metode Tampil()
yang digunakan, berikut adalah beberapa cara pemanggilan metode tersebut dengan
argumen bernama:
//
Menspesifikasi semua argumen dengan nama.
Tampil(berhenti:
10, str: "ini adalah pengujian", mulai: 0);
//
Menjadikan mulai sebagai default.
Tampil
(berhenti: 10, str: " ini adalah pengujian");
//
Menspesifikasi string dengan posisi, berhenti dengan nama, dan
//
menjadikan awal sebagai default.
Tampil ("ini
adalah pengujian", berhenti: 10);
Metode
Main( )
Sampai sejauh
ini, Anda hanya menggunakan satu format dari Main(). Namun, metode ini mempunyai beberapa format teroverload.
Beberapa di antaranya dapat dipakai untuk menghasilkan nilai balik, dan
beberapa lagi dapat meneriman argumen. Masing-masing versi teroverload dari Main() akan dibahas di sini.
Nilai
Balik Dari Main( )
Ketika sebuah
program berakhir, Anda dapat menghasilkan nilai balik kepada proses pemanggil
(seringkali proses pemanggil adalah sistem operasi) dari Main(). Untuk melakukannya, Anda dapat menggunakan formati Main() ini:
static
int Main( )
Perhatikan bahwa
metode di atas tidak dideklarasikan sebagai void, tetapi Main()
versi ini mempunyai nilai balik bertipe
int. Biasanya, nilai balik dari Main()
mengindikasikan bahwa program berakhir secara normal atau berakhir secara
mendadak karena kondisi abnormal. Secara konvensional, nilai balik 0 biasanya
mengindikasikan terminasi normal. Nilai balik selain 0 mengindikasikan beberapa
jenis error terjadi.
Melewatkan
Argumen Kepada Main( )
Banyak program
dapat menerima apa yang dinamakan argumen command-line. Argumen command-line merupakan
informasi yang secara langsung ditempatkan setelah nama program pada command-line ketika program dieksekusi. Pada program C#, argumen ini
kemudian dilewatkan kepada metode Main().
Untuk menerima argumen tersebut, Anda harus menggunakan salah satu format Main() berikut:
static
void Main(string[ ] args)
static
int Main(string[ ] args)
Format pertama
menghasilkan void; format kedua
dapat dipakai untuk menghasilkan sebuah nilai balik bertipe int. Pada kedua format, argumen command-line disimpan sebagai string di dalam array string yang dilewatkan kepada Main().
Panjang dari array args sama dengan jumlah argumen command-line.
Sebagai contoh,
program berikut menampilkan semua argumen command-line:
// Menampilkan semua
informasi command-line.
using System;
class DemoCL {
static
void Main(string[] args) {
Console.WriteLine("Ada sebanyak " + args.Length +
" argumen command-line.");
Console.WriteLine("Semua argumen tersebut adalah: ");
for
(int i = 0; i < args.Length; i++)
Console.WriteLine(args[i]);
}
}
Jika DemoCL dieksekusi seperti ini:
DemoCL satu dua tiga
maka Anda akan
melihat keluaran:
Ada sebanyak 3 argumen command-line.
Semua argumen tersebut adalah:
satu
dua
tiga
Untuk memahami
bagaimana argumen command-line
digunakan, perhatikan program berikut. Program ini menggunakan cipher substitusi
sederhana untuk mengkode atau mendekondekan pesan. Pesan yang akan dikodekan
dispesifikasi pada command line. Cipher ini sangat sederhana: Untuk
mengkodekan sebuah kata, setiap huruf diinkremen sebesar 1. Jadi, A menjadi B,
dan seterusnya. Untuk mendekode, setiap huruf didekremen sebesar 1.
// Mengkodekan atau
mendekodekan pesan menggunakan cipher substitusi sederhana.
using System;
class Cipher {
static
int Main(string[] args) {
// Melihat apakah argumen ada.
if(args.Length
< 2) {
Console.WriteLine("Kegunaan: enkode/dekode kata1
[kata2...kataN]");
return
1; // nilai balik jika kode gagal
}
// Jika args ada, arg pertama harus
dienkodekan atau didekodekan.
if(args[0]
!= "enkode" & args[0]
!= "dekode") {
Console.WriteLine("Arg
pertama harus dienkodekan atau didekodekan.");
return
1; // nilai balik jika kode gagal
}
// Enkode atau dekode pesan.
for(int n=1; n < args.Length; n++) {
for(int i=0; i < args[n].Length; i++) {
if
(args[0] == "enkode")
Console.Write((char)(args[n][i]
+ 1));
else
Console.Write((char)(args[n][i]
- 1));
}
Console.Write("
");
}
Console.WriteLine();
return
0;
}
}
Untuk
menggunakan program, Anda diminta untuk menspesifikasi perintah “enkode” atau
“dekode” diikuti dengan frase yang ingin Anda enkript atau dekript. Berikut adalah
dua contoh keluaran program:
C:>Cipher enkode satu dua
tbuv evb
C:>Cipher dekode tbuv evb
satu dua
Rekursi
Dalam C#, sebuah
metode dapat memanggil dirinya sendiri. Proses ini dikenal dengan rekursi, dan
metode yang memanggil dirinya sendiri tersebut dikenal dengan rekursif. Secara
umum, rekursi merupakan proses pendefinisian secara sirkular. Komponen kunci dari
suatu metode rekursif adalah bahwa ia memuat sebuah statemen yang mengeksekusi
pemanggilan terhadap dirinya. Rekursi merupakan mekanisme kendali yang tangguh.
Contoh klasik
dari rekursi adalah komputasi atas faktorial dari sebuah angka. Faktorial dari
sebuah angka N adalah hasil dari 1 x 2 x...x N-1 x N. Program berikut
menunjukkan cara rekursif untuk menghitung faktorial dari sebuah angka. Sebagai
perbandingan, cara tak-rekursif juga disertakan di dalam program.
// Sebuah contoh rekursi
sederhana.
using System;
class Faktorial {
// Ini adalah sebuah metode rekursif.
public
int FaktR(int n)
{
int
hasil;
if
(n == 1) return 1;
hasil = FaktR(n - 1) * n;
return
hasil;
}
// Ini adalah sebuah metode iteratif
ekivalen.
public int FaktI(int n)
{
int
t, hasil;
hasil = 1;
for
(t = 1; t <= n; t++) hasil *= t;
return
hasil;
}
}
class Rekursi {
static
void Main() {
Faktorial f = new Faktorial();
Console.WriteLine("Faktoral menggunakan metode rekursif.");
Console.WriteLine("Faktorial dari 3 adalah " +
f.FaktR(3));
Console.WriteLine("Faktorial dari 4 adalah " +
f.FaktR(4));
Console.WriteLine("Faktorial dari 5 adalah " + f.FaktR(5));
Console.WriteLine();
Console.WriteLine("Faktorial menggunakan metode iteratif.");
Console.WriteLine("Faktorial dari 3 adalah " +
f.FaktI(3));
Console.WriteLine("Faktorial dari 4 adalah " +
f.FaktI(4));
Console.WriteLine("Faktorial dari 5 adalah " +
f.FaktI(5));
}
}
Keluaran program
ditampilkan di sini:
Faktoral menggunakan metode rekursif.
Faktorial dari 3 adalah 6
Faktorial dari 4 adalah 24
Faktorial dari 5 adalah 120
Faktorial menggunakan metode iteratif.
Faktorial dari 3 adalah 6
Faktorial dari 4 adalah 24
Faktorial dari 5 adalah 120
Operasi dari
metode tak-rekursif FaktI() harusnya
sudah bisa dimengerti dengan jelas. Metode tersebut menggunakan sebuah loop
mulai dari 1 dan secara progresif mengalikan setiap angka dengan hasil
perkalian bergerak.
Operasi dari
metode rekursif FaktR() sedikit
lebih kompleks. Ketika FaktR() dipanggil
dengan argumen 1, metode menghasilkan 1; sebaliknya, ia menghasilkan FaktR(n-1)*n. Untuk mengevaluasi
ekspresi ini, FaktR() dipanggil
dengan n-1. Proses ini berulang
sampai n sama dengan 1. Sebagai contoh, ketika faktorial dari 2 dihitung,
pemanggilan pertama terhadap FaktR()
akan menyebabkan pemanggilan kedua dilakukan dengan argumen 1. Pemanggilan
kedua tersebut menghasilkan 1, yang kemudian dikalikan dengan 2 (nilai awal
dari 2). Hasil yang diperoleh adalah 2. Anda bisa menyisipkan statemen WriteLine() ke dalam FaktR() untuk membuktikan level rekursi
atas setiap pemanggilan.
Ketika sebuah
metode memanggil dirinya sendiri, variabel dan parameter lokal baru
dialokasikan pada tumpukan, dan kode metode dieksekusi dengan variabel
tersebut. Pemanggilan rekursif tidak membuat salinan baru dari metode. Hanya
argumen yang baru.
Berikut
merupakan contoh lain dari rekursi. Metode TampilBalik()
menggunakan rekursi untuk menampilkan argumen stringnya dengan gaya
terbalik:
// Menampilkan sebuah string
secara terbalik menggunakan rekursi.
using System;
class StrBalik {
// Menampilkan string secara muncur.
public
void TampilBalik(string str)
{
if
(str.Length > 0)
TampilBalik(str.Substring(1,
str.Length - 1));
else
return;
Console.Write(str[0]);
}
}
class DemoStrBalik {
static
void Main() {
string s = "danau toba terbesar se asia tenggara";
StrBalik rsOb = new StrBalik();
Console.WriteLine("String asli: " + s);
Console.Write("String terbalik: ");
rsOb.TampilBalik(s);
Console.WriteLine();
}
}
Berikut adalah
keluaran program:
String asli: danau toba terbesar se asia tenggara
String terbalik: araggnet aisa es rasebret abot uanad
Memahami
static
Ada waktu dimana
Anda ingin mendefinisikan sebuah anggota yang yang akan digunakan secara
independen oleh sembarang objek dari kelas tersebut. Normalnya, suatu anggota
kelas bisa diakses melalui sebuah objek dari kelasnya, tetapi dimungkin pula
untuk menciptakan suatu anggota kelas yang bisa dipakai tanpa memerlukan
instans kelas atau referensi. Untuk menciptakan anggota kelas semacam itu,
pendeklarasiannya diawali dengan katakunci static.
Ketika sebuah anggota kelas dideklarasikan static,
maka ia dapat diakses sebelum sembarang objek dari kelasnya diciptakan dan
tanpa memerlukan referensi objek. Anda bisa mendeklarasikan metode maupun
variabel sebagai static. Contoh yang
umum dijumpai dari anggota static
adalah Main(), yang dideklarasikan static karena ia harus dipanggil oleh
sistem operasi ketika program Anda mulai dikompilasi.
Di luar kelas,
untuk menggunakan anggota static,
Anda perlu menspesifikasi nama dari kelasnya diikuti dengan operator dot. Tidak
ada objek yang perlu diciptakan. Anggota static
tidak dapat diakses melalui sebuah referensi objek. Ia harus diakses melalui
nama kelasnya. Sebagai contoh, jika Anda ingin menugaskan nilai 10 kepada
sebuah variabel static, hitung, yang merupakan bagian dari
kelas Pewaktu, maka Anda bisa
menggunakan baris kode ini:
Pewaktu.hitung = 10;
Format ini sama
dengan yang digunakan untuk mengakses variabel instans melalui sebuah objek,
tetapi yang tidak sama adalah nama kelas yang digunakan. Sebuah metode static dapat juga dipanggil dengan cara
yang sama, menggunakan operator dot dan nama kelas.
Variabel yang
dideklarasikan static, secara
esensial, adalah variabel global. Ketika objek dari kelasnya dideklarasikan, tidak
ada salinan dari variabel static
yang dibuat. Semua instans dari kelasnya menggunakan bersama variabel static. Variabel static diinisialisasi
sebelum kelasnya digunakan. Jika tidak ada penginisialisasi eksplisit
dispesifikasi, maka variabel static
diinisialisasi dengan nol untuk tipe numerik, dengan null untuk tipe referensi, atau dengan false untuk tipe bool. Jadi, variabel static selalu memiliki nilai.
Perbedaan antara
metode static dan metode normal
adalah bahwa metode static dapat
dipanggil melalui nama kelasnya, tanpa melalui sembarang instans dari kelas
yang sedang diciptakan. Anda telah melihat contohnya: metode Sqrt(), yang merupakan sebuah metode static di dalam kelas System.Math.
Berikut adalah
sebuah contoh yang mendeklarasikan variabel static dan metode static:
// Menggunakan static.
using System;
class DemoStatic{
// Sebuah variabel static.
public
static int Nil = 100;
// Sebuah metode static.
public
static int NilBagi2()
{
return
Nil / 2;
}
}
class SDemo {
static
void Main() {
Console.WriteLine("Nilai awal dari DemoStatic.Nil adalah
"
+ DemoStatic.Nil);
DemoStatic.Nil = 8;
Console.WriteLine("DemoStatic.Nil sekarang adalah " +
DemoStatic.Nil);
Console.WriteLine("DemoStatic.NilBagi2(): " +
DemoStatic.NilBagi2());
}
}
Keluaran program
ditampilkan di sini:
Nilai awal dari DemoStatic.Nil adalah 100
DemoStatic.Nil sekarang adalah 8
DemoStatic.NilBagi2(): 4
Seperti yang
ditampilkan keluaran, variabel static
diinisialisasi sebelum sembarang objek dari kelasnya diciptakan. Ada beberapa
batasan pada metode static:
·
Metode static tidak memiliki referensi this. Ini karena metode static
tidak dieksekusi relatif terhadap sembarang objek.
·
Metode static dapat memanggil secara langsung metode static lain dari kelasnya. Ia tidak bisa memanggil secara langsung
metode instans dari kelasnya. Alasannya adalah karena metode instans beroperasi
pada objek spesifik, sedangkan metode static
tidak dipanggil pada suatu objek spesifik.
·
Batasan yang sama berlaku pada data static. Sebuah data static hanya dapat secara langsung
mengakses data static lain yang didefinisikan oleh kelasnya.
Sebagai contoh, dalam kelas berikut, metode static, NilBagiDenom() adalah ilegal:
class
StaticError {
public int Denom = 3; // sebuah
variabel instans normal
public static int Nil = 1024; // sebuah variabel static
/* Error! Tidak
bisa secara langsung mengakses variabel tak-static
dari dalam
metode static. */
static int NilBagiDenom() {
return Nil/Denom; // Tidak akan bisa
dikompilasi!
}
}
Di sini, Denom merupakan sebuah variabel instans
biasa yang tidak bisa diakses dari dalam sebuah metode static. Namun, penggunaan Nil
di dalam metode static diijinkan
karena ia adalah variabel static.
Adalah penting
memahami bahwa metode static dapat
memanggil metode instans dan mengakses variabel instans dari kelasnya jika
dilakukan melalui objek dari kelas itu. Metode static hanya tidak bisa mengakses variabel instans atau metode
instans dapat kualifikasi objek. Sebagai contoh, fragmen kode berikut dapat
diterima:
class KelasKu
{
// Sebuah metode
tak-static.
void MetTakStatik() {
Console.WriteLine("Di dalam
MetTakStatik ().");
}
/* Dapat
memanggil metode tak-static melalui sebuah
referensi
objek dari dalam metode static. */
public static void MetStatik(KelasKu
ob) {
ob. MetTakStatik();
// hal ini OK
}
}
Di sini, MetTakStatik() dipanggil oleh MetStatik() melalui ob, yang merupakan sebuah objek bertipe
KelasKu.
Karena bidang static independen dari sembarang objek
spesifik, bidang tersebut berguna ketika Anda perlu mendapatkan informasi yang
diterapkan pada keseluruhan kelas. Berikut merupakan contoh dari situasi
semacam itu. Program menggunakan sebuah bidang static untuk mendapatkan jumlah objek yang eksis:
// Menggunakan sebuah bidang
static untuk menghitung instans.
using System;
class HitungInst {
static
int hitung = 0;
// Menginkremen hitung ketikan objek
diciptakan.
public
HitungInst()
{
hitung++;
}
// Mendekremen hitung ketika objek
dihancurkan.
~HitungInst()
{
hitung--;
}
public
static int GetHitung()
{
return
hitung;
}
}
class DemoHitung {
static
void Main() {
HitungInst ob;
for
(int i = 0; i < 10; i++)
{
ob = new HitungInst();
Console.WriteLine("Jumlah
objek saat ini: " + HitungInst.GetHitung());
}
}
}
Keluaran program
adalah sebagai berikut:
Jumlah objek saat ini: 1
Jumlah objek saat ini: 2
Jumlah objek saat ini: 3
Jumlah objek saat ini: 4
Jumlah objek saat ini: 5
Jumlah objek saat ini: 6
Jumlah objek saat ini: 7
Jumlah objek saat ini: 8
Jumlah objek saat ini: 9
Jumlah objek saat ini: 10
Setiap kali
sebuah objek bertipe HitungInst
diciptakan, bidang static, hitung, diinkremen. Setiap kali sebuah
objek dihancurkan, hitung,
didekremen. Jadi, hitung selalu memuat jumlah objek yang sedang eksis. Hal ini
dimungkinak hanya melalui penggunaan bidang static.
Berikut adalah
satu contoh lebih kompleks yang menggunakan static. Pada awal bab ini, Anda telah melihat bagaimana sebuah
metode Factory digunakan untuk
menciptakan objek. Pada contoh tersebut, metode Factory merupakan sebuah metode tak-static, yang berarti bahwa ia hanya bisa dipanggil melalui
referensi objek. Ini juga berarti bahwa sebuah objek default dari kelas
tersebut perlu diciptakan sehingga Factory
dapat dipanggil. Namun, ada cara yang
lebih baik dalam mengimplementasikan hal ini. Caranya adalah dengan menjadikan Factory sebagai static, sehingga dapat dipanggil tanpa perlu menciptakan objek.
Berikut adalah program untuk merealisasikan gagasan ini:
// Menggunakan static Factory.
using System;
class KelasKu {
int
a, b;
//
Menciptakan sebuah kelas Factory untuk KelasKu.
static
public KelasKu Factory(int i, int j)
{
KelasKu t = new KelasKu();
t.a = i;
t.b = j;
return
t; // menghasilkan sebuah objek
}
public
void
Tampil()
{
Console.WriteLine("a dan b: " + a + " " +
b);
}
}
class MembuatObjek {
static
void Main() {
int
i, j;
// Menghasilkan objek menggunakan
Factory.
for
(i = 0, j = 10; i < 10; i++, j--)
{
KelasKu ob = KelasKu.Factory(i, j);
// mendapatkan sebuah object
ob.Tampil();
}
Console.WriteLine();
}
}
Berikut adalah
keluaran program:
a dan b: 0 10
a dan b: 1 9
a dan b: 2 8
a dan b: 3 7
a dan b: 4 6
a dan b: 5 5
a dan b: 6 4
a dan b: 7 3
a dan b: 8 2
a dan b: 9 1
Pada versi ini, Factory() dipanggil melalui nama
kelasnya pada baris kode ini:
KelasKu ob =
KelasKu.Factory(i, j);
Tidak diperlukan
untuk menciptakan sebuah objek KelasKu
sebelum menggunakan Factory().
Konstruktor
static
Sebuah
konstruktor dapat dispesifikasi sebagai static.
Konstruktor static secara umum
dipakai untuk menginisialisasi fitur yang dapat diterapkan pada sebuah kelas,
bukan pada instansnya. Jadi, konstruktor static
dipakai untuk menginisialisasi aspek-aspek sebuah kelas sebelum sembarang objek
dari kelas tersebut diciptakan. Berikut adalah contoh sederhananya:
// Menggunakan sebuah
konstruktor static.
using System;
class Konst
{
public
static int alfa;
public
int beta;
// Sebuah konstruktor static.
static
Konst()
{
alfa = 99;
Console.WriteLine("Di dalam konstruktor static.");
}
// Sebuah konstruktor instans.
public
Konst()
{
beta = 100;
Console.WriteLine("Di dalam konstruktor instans.");
}
}
class DemoKonst
{
static
void Main()
{
Konst ob = new Konst();
Console.WriteLine("Konst.alfa: " + Konst.alfa);
Console.WriteLine("ob.beta: " + ob.beta);
}
}
Keluaran program
adalah sebagai berikut:
Di dalam konstruktor static.
Di dalam konstruktor instans.
Konst.alfa: 99
ob.beta: 100
Perhatikan bahwa
konstruktor static dipanggil secara
otomatis sebelum konstruktor instans. Hal ini dapat digeneralisir. Pada semua
kasus, konstruktor static akan
dieksekusi sebelum sembarang konstruktor instans. Di samping itu, konstruktor static tidak memiliki pemodifikasi
akses (jadi, menggunakan pemodifikasi akses default) dan tidak dapat dipanggil
oleh program Anda.
Kelas
static
Sebuah kelas
dapat dideklarasikan static. Ada
fitur katakunci dari kelas static.
Pertama, tidak ada objek kelas static
yang dapat diciptakan. Kedua, kelas static haru hanya memuat anggota-anggota static. Kelas static diciptakan dengan memodifikasi deklarasi kelas dengan
katakunci static, ditunjukkan di
sini:
static class nama-kelas { // ...
Di dalam kelas,
semua anggota harus dispesifikasi secara eksplisit sebagai static. Membuat kelas menjadi static
tidak secara otomatis membuat anggota-anggotanya menjadi static pula.
Kelas static mempunyai dua kegunaan utama.
Pertama, kelas static diperlukan
ketika menggunakan metode ekstensi. Metode ekstensi berelasi dengan LINQ.
Kedua, kelas static digunakan untuk
memuat koleksi yang memuat metode-metode static
yang berelasi. Kegunaan kedua ini akan didemonstrasikan di sini.
Contoh berikut
menggunakan kelas static, yang
bernama NumerikFn untuk memuat
beberapa metode static yang beroperasi pada sebuah nilai numerik. Karena semua
anggota dari kelas NumerikFn
dideklarasikan static, kelas juga
harus dideklarasikan static, untuk
menghindarinya dari instansiasi. Jadi, NumerikFn
hanya berperan organisasional, yang menyediakan cara untuk mengelompokkan
beberapa metode yang berelasi.
// Demonstrasi sebuah kelas
static.
using System;
static class NumerikFn {
// Menghasilkan kebalikan dari sebuah nilai.
static
public double Kebalikan(double
angka) {
return
1 / angka;
}
// Menghasilkan bagian fraksional dari sebuah
angka.
static
public double BagianFrak(double
angka) {
return
angka - (int)angka;
}
// Menghasilkan true jika angka genap.
static
public bool ApaGenap(double
angka) {
return
(angka % 2) == 0 ? true : false;
}
// Menghasilkan true jika angka ganjil.
static
public bool ApaGanjil(double
angka) {
return
!ApaGenap(angka);
}
}
class DemoKelasStatic {
static
void Main() {
Console.WriteLine("Kebalikan dari 5 adalah " +
NumerikFn.Kebalikan(5.0));
Console.WriteLine("Bagian fraksional dari 4.234 adalah
" +
NumerikFn.BagianFrak(4.234));
if
(NumerikFn.ApaGenap(10))
Console.WriteLine("10 adalah genap.");
if
(NumerikFn.ApaGanjil(5))
Console.WriteLine("5 adalah ganjil.");
// Pencobaan berikut adalah untuk
menciptakan sebuah instans dari
// NumerikFn yang akan menyebabkan error.
// NumerikFn ob = new NumerikFn(); // Salah!
}
}
Keluaran program
ditampilkan di sini:
Kebalikan dari 5 adalah 0.2
Bagian fraksional dari 4.234 adalah 0.234
10 adalah genap.
5 adalah ganjil.
Satu hal penting
terakhir: Meskipun kelas static
tidak bisa memiliki konstruktor instans, ia bisa mempunyai konstruktor static.
No comments:
Post a Comment