Saturday, December 24, 2016

Bab 9. Pemrograman C# Belajar Dari Contoh



9. Indekser dan Properti







Bab ini akan membahas dua tipe anggota kelas yang satu sama lain memiliki relasi yang sangat dekat: indekser dan properti. Masing-masing mempunyai kontribusi dalam mengintegrasikan dan meningkatkan kehandalan sistem tipe C#. Indekser merupakan suatu mekanisme sehingga sebuah objek dapat diindeks seperti array. Properti berperan untuk mengelola akses terhadap data instans suatu kelas. Keduanya berelasi satu sama lain karena bergantung pada fitur C# yang lain: aksesor.


Indekser
Indekser membuat sebuah objek diindeks seperti layaknya array. Kegunaan utama indekser adalah untuk mendukung penciptaan array terspesialisasi, yang memiliki satu atau dua kekangan. Indekser dapat mempunyai satu atau lebih dimensi. Pembahasan akan dimulai dengan indekser satu dimensi.


Menciptakan Indekser Satu Dimensi
Indekser satu dimensi mempunyai bentuk umum sebagai berikut:

tipe-elemen this[int indeks] {
  // Aksesor get
  get {
      // menjadikan nilai yang dispesifikasi oleh indeks sebagai nilai balik
  }

  // Aksesor set
  set {
     // menetapkan nilai yang dispesifikasi oleh indeks
  }
}

Di sini, tipe-elemen merupakan tipe elemen dari indekser. Jadi, setiap elemen yang diakses oleh indekser bertipe tipe-elemen. Tipe ini berkaitan dengan tipe elemen suatu array. Parameter indeks menerima indeks elemen yang sedang diakses. Secara teknis, parameter ini tidak harus bertipe int, tetapi karena indeks secara umum dipakai untuk pengindeksan array, penggunaan tipe integer paling umum digunakan.

Di dalam tubuh indekser, terdapat dua aksesor yang didefinisikan, dinamakan dengan get dan set. Aksesor sama dengan sebuah metode kecuali bahwa ia tidak mendeklarasikan tipe nilai balik atau parameter. Aksesor ini secara otomatis dipanggil ketika indekser dipakai, dan kedua aksesor menerima indeks sebagai parameter. Jika indekser berada di sisi kiri suatu statemen penugasan, maka aksesor set dipanggil dan elemen yang dispesifikasi oleh indeks harus ditetapkan. Sebaliknya, aksesor get dipanggil dan nilai yang berkaitan dengan indeks dijadikan nilai balik. Metode set juga menerima sebuah parameter implisit, dinamakan value, yang memuat nilai yang sedang ditugaskan kepada indeks yang dispesifikasi.

Salah satu keuntungan dari indekser adalah bahwa Anda dapat mengendalikan bagaimana array diakses. Berikut disajikan sebuah contoh. Pada program berikut, Kelas ArrayGagalHalus mengimple-mentasikan sebuah array yang mengakses di luar batas indeksnya. Ini dilakukan dengan mengenkapsulasi array tersebut sebagai anggota privat kelas, sehingga pengaksesan array hanya bisa dilakukan melalui indekser. Dengan pendekatan ini, semua percobaan untuk mengakses array di luar batasnya akan dicegah. Karena ArrayGagalHalus menggunakan indekser, array dapat diakses menggunakan notasi array biasa.

// Menggunakan indekser untuk menciptakan array gagal-halus.
using System;

class ArrayGagalHalus {
  int[] a; // referensi ke array
  public int Panjang; // Panjang dideklarasikan public
  public bool ErrFlag; // mengindikasikan keluaran operasi terakhir

  // Mengkonstruksi array dengan ukuran tertentu.
  public ArrayGagalHalus(int ukuran) {
    a = new int[ukuran];
    Panjang = ukuran;
  }

  // Ini adalah indekser untuk ArrayGagalHalus.
  public int this[int indeks] {
    // Ini adalah aksesor get.
    get {
      if (ok(indeks))
      {
        ErrFlag = false;
        return a[indeks];
      } else {
        ErrFlag = true;
        return 0;
    }
  }

  // Ini adalah aksesor set.
  set
  {
    if (ok(indeks))
    {
      a[indeks] = value;
      ErrFlag = false;
    }
    else ErrFlag = true;
    }
  }

  // Menghasilkan true jika indeks di dalam batas.
  private bool ok(int indeks)
  {
    if (indeks >= 0 & indeks < Panjang) return true;
    return false;
  }
}

// Mendemonstrasikan array gagal-halus.
class DemoGH {
  static void Main(){
    ArrayGagalHalus fs = new ArrayGagalHalus(5);
    int x;
  
    // Menampilkan gagal halus.
    Console.WriteLine("Gagal secara halus.");
      for (int i = 0; i < (fs.Panjang * 2); i++)
        fs[i] = i * 10;
     
      for (int i = 0; i < (fs.Panjang * 2); i++)
      {
        x = fs[i];
        if (x != -1) Console.Write(x + " ");
      }
      Console.WriteLine();
   
      // Sekarang, menampilkan kegagalan.
      Console.WriteLine("\nGagal dengan laporan error.");
      for (int i = 0; i < (fs.Panjang * 2); i++)
      {
        fs[i] = i * 10;
        if (fs.ErrFlag)
          Console.WriteLine("fs[" + i + "] di luar batas array");
      }
     
      for (int i = 0; i < (fs.Panjang * 2); i++)
      {
        x = fs[i];
        if (!fs.ErrFlag) Console.Write(x + " ");
        else
          Console.WriteLine("fs[" + i + "] di luar batas array");
      }
  }
}

Keluaran program ditampilkan di sini:

Gagal secara halus.
0 10 20 30 40 0 0 0 0 0

Gagal dengan laporan error.
fs[5] di luar batas array
fs[6] di luar batas array
fs[7] di luar batas array
fs[8] di luar batas array
fs[9] di luar batas array
0 10 20 30 40 fs[5] di luar batas array
fs[6] di luar batas array
fs[7] di luar batas array
fs[8] di luar batas array
fs[9] di luar batas array

Indekser mencegah pengaksesan di luar batas array. Akan ditengok lebih dekat pada setiap bagian indekser. Dimulai dengan baris ini:

public int this[int indeks] {

Ini mendeklarasikan sebuah indeks yang beroperasi pada elemen int. Indeks ini dilewatkan di dalam indeks. Indekser dideklarasikan publik, sehingga dapat digunakan oleh sembarang kode di luar kelasnya.

Aksesor get ditampilkan di sini:

  // Ini adalah indekser untuk ArrayGagalHalus.
  public int this[int indeks] {
    // Ini adalah aksesor get.
    get {
      if (ok(indeks))
      {
        ErrFlag = false;
        return a[indeks];
      } else {
        ErrFlag = true;
        return 0;
    }
  }

Aksesor get mencegah error batas array dengan pertama-tama memastikan bahwa indeks tidak di luar batas array. Pemeriksaan rentang ini dilakukan oleh metode ok(), yang menghasilkan nilai balik true jika indeks valid dan false jika sebaliknya. Jika indeks yang dispesifikasi berada di dalam retang yang diijinkan, elemen terkait dengan indeks tersebut akan dijadikan nilai balik. Jika ia berada di luar batas, tidak ada operasi apapun yang dilakukan dan pengaksesan array di luar batas tidak terjadi. Pada versi ArrayGagalHalus ini, sebuah variabel, dinamakan ErrFlag, memuat keluaran setiap operasi. Bidang ini diuji pada tiap operasi untuk menentukan kegagalan atau keberhasilan operasi. (Pada Bab 12, Anda akan melihat cara lebih baik dalam menangani error dengan menggunakan penanganan eksepsi C#).

Aksesor set ditampilkan di sini. Aksesor ini juga mencegah error di-luar-batas:

// Ini adalah aksesor set.
  set
  {
    if (ok(indeks))
    {
      a[indeks] = value;
      ErrFlag = false;
    }
    else ErrFlag = true;
    }
  }

Di sini, jika indeks di dalam rentang yang diijinkan, nilai yang dilewatkan di dalam value ditugaskan kepada elemen terkait. Sebaliknya, ErrFlag akan ditetapkan true. Ingat bahwa di dalam sebuah metode aksesor, value merupakan parameter implisit yang memuat nilai yang sedang ditugaskan. Anda tidak perlu mendeklarasikannya.

Sebuah indekser tidak perlu mendukung kedua get dan set. Anda dapat menciptakan indekser read-only dengan hanya mengimplamentasikan aksesor get. Anda dapat menciptakan indekser write-only dengan hanya mengimplementasikan aksesor set.


Indekser Dapat Dioverload
Sebuah indekser dapat dioverload. Berikut adalah sebuah contoh yang mengoverload indekser ArrayGagalHalus untuk indeks bertipe double. Indekser double membulatkan indeksenya menjadi nilai integer terdekat.

// Mengoverload indekser ArrayGagalHalus.
using System;

class ArrayGagalHalus {
  int[] a; // referensi ke array
  public int Panjang; // Panjang dideklarasikan public
  public bool ErrFlag; // mengindikasikan keluaran operasi terakhir

  // Mengkonstruksi array dengan ukuran tertentu.
  public ArrayGagalHalus(int ukuran) {
    a = new int[ukuran];
    Panjang = ukuran;
  }

  // Ini adalah indekser untuk ArrayGagalHalus.
  public int this[int indeks] {
    // Ini adalah aksesor get.
    get {
      if (ok(indeks))
      {
        ErrFlag = false;
        return a[indeks];
      } else {
        ErrFlag = true;
        return 0;
    }
  }

  // Ini adalah aksesor set.
  set
  {
    if (ok(indeks))
    {
      a[indeks] = value;
      ErrFlag = false;
    }
    else ErrFlag = true;
    }
  }

  /* Ini adalah indekser lain untuk ArrayGagalHalus.
     Indeks ini mengambil argumen double. Indeks tersebut
     membulatkan argumen menjadi indeks integer terdekat. */
  public int this[double idx]
  {
    // Ini adalah aksesor get.
    get
    {
      int indeks;
     
      // Membulatkan ke int terdekat.
      if ((idx - (int)idx) < 0.5) indeks = (int)idx;
      else indeks = (int)idx + 1;
     
      if (ok(indeks))
      {
        ErrFlag = false;
        return a[indeks];
      }
     
      else
      {
        ErrFlag = true;
        return 0;
      }
    }
     
    // Ini adalah aksesor set.
    set
    {
      int indeks;
     
      // Membulatkan ke int terdekat.
      if ((idx - (int)idx) < 0.5) indeks = (int)idx;
      else indeks = (int)idx + 1;
     
      if (ok(indeks))
      {
        a[indeks] = value;
        ErrFlag = false;
      }
     
      else ErrFlag = true;
    }
  }

  // Menghasilkan true jika indeks di dalam batas.
  private bool ok(int indeks)
  {
    if (indeks >= 0 & indeks < Panjang) return true;
    return false;
  }
}

// Mendemonstrasikan array gagal-halus.
class DemoGH {
  static void Main(){
    ArrayGagalHalus fs = new ArrayGagalHalus(5);
  
    // Menempatkan beberapa nilai di dalam fs.
    for(int i=0; i < fs.Panjang; i++)
    fs[i] = i;

    // Sekarang indeks dengan int dan double.
    Console.WriteLine("fs[1]: " + fs[1]);
    Console.WriteLine("fs[2]: " + fs[2]);
    Console.WriteLine("fs[1.1]: " + fs[1.1]);
    Console.WriteLine("fs[1.6]: " + fs[1.6]);
  }
}

Program ini menghasilkan keluaran sebagai berikut:

fs[1]: 1
fs[2]: 2
fs[1.1]: 1
fs[1.6]: 2

Seperti ditampilkan keluaran, indeks double dibulatkan ke nilai integer terdekat. Dapat diperhatikan bahwa 1.1 dibulatkan ke 1, dan 1.6 dibulatkan ke 2.


Indekser Tidak Harus Memerlukan Array
Penting untuk dipahami bahwa tidak ada keharusan bagi indekser untuk beroperasi pada sebuah array. Indekser hanya harus mempunyai fungsionalitas “seperti-array”. Sebagai contoh, program berikut mempunyai indekser yang berperan sebagai array read-only yang memuat 2 pangkat 0 sampai 2 pangkat 15. Perhatikan bahwa tidak ada array aktual. Indekser hanya menghitung nilai yang diinginkan untuk indeks tertentu.

// Indekser tidak harus beroperasi pada array aktual.
using System;

class DuaPangkat
{
    /* Mengakses array logikal yang memuat
       2 pangkat 0 sampai 15. */
    public int this[int indeks]
    {
        // Menghitung dan menghasilkan 2 pangkat sekian.
        get
        {
            if ((indeks >= 0) && (indeks < 16)) return pangkat(indeks);
            else return -1;
        }
        // Tidak ada aksesor set.
    }
    int pangkat(int p)
    {
        int hasil = 1;
        for (int i = 0; i < p; i++)
            hasil *= 2;
        return hasil;
    }
}

class GunakanPangkatDua {
    static void Main() {
        DuaPangkat pangkat = new DuaPangkat();
        Console.Write("2 pangkat 0 sampai 2 pangkat 8: ");
       
        for (int i = 0; i < 8; i++)
            Console.Write(pangkat[i] + " ");
       
        Console.WriteLine();
        Console.Write("Berikut adalah beberapa error: ");
        Console.Write(pangkat[-1] + " " + pangkat[17]);
        Console.WriteLine();
    }
}

Keluaran program ditunjukkan di sini:

2 pangkat 0 sampai 2 pangkat 8: 1 2 4 8 16 32 64 128
Berikut adalah beberapa error: -1 -1

Perhatikan bahwa indekser untuk DuaPangkat mencantumkan sebuah aksesor get, tetapi tidak memiliki aksesor set. Seperti yang telah dijelaskan sebelumnya, ini merupakan indekser read-only. Jadi, sebuah objek DuaPangkat dapat dipakai di sisi kanan penugasan, tetapi tidak di sisi kiri. Sebagai contoh, jika Anda mencoba menambahkan statemen ini pada program, maka program tidak akan bisa dikompilasi:

pangkat[0] = 11; // tidak akan bisa dikompilasi

Statemen ini akan menyebabkan error kompilasi karena tidak ada aksesor set yang didefinisikan untuk indekser.

Ada dua batasan penting dalam menggunakan indekser. Pertama, karena indekser tidak mendefinisikan lokasi penyimpanan, nilai yang dihasilkan oleh indekser tidak bisa dilewatkan sebagai parameter ref atau out kepada sebuah metode. Kedua, indekser harus merupakan anggota instans dari kelasnya; ia tidak bisa dideklarasikan static.


Indekser Multidimensi
Anda dapat menciptakan indekser untuk array multidimensi. Sebagai contoh, berikut adalah sebuah array multidimensi gagal-halus. Perhatikan secara dekat cara indekser dideklarasikan.

// Sebuah array multidimensi gagal-halus.
using System;

class Array2DGagalHalus {
  int[,] a; // referensi ke array 2D
  int baris, kolom; // dimensi
  public int Panjang; // Panjang dideklarasikan public
  public bool ErrFlag; // mengindikasikan keluaran operasi terakhir

  // Mengkonstruksi array dari dimensi yang diberikan.
  public Array2DGagalHalus(int r, int c) {
    baris = r;
    kolom = c;
    a = new int[baris, kolom];
    Panjang = baris * kolom;
  }

  // Ini adalah indekser untuk Array2DGagalHalus.
  public int this[int indeks1, int indeks2] {
   
    // Ini adalah aksesor get.
    get {
      if(ok(indeks1, indeks2)) {
        ErrFlag = false;
        return a[indeks1, indeks2];
      } else {
        ErrFlag = true;
        return 0;
      }
    }

    // Ini adalah aksesor set.
    set {
      if(ok(indeks1, indeks2)) {
        a[indeks1, indeks2] = value;
        ErrFlag = false;
      }
      else ErrFlag = true;
    }
  }

  // Menghasilkan true jika indeks di dalam batas.
  private bool ok(int indeks1, int indeks2) {
    if(indeks1 >= 0 & indeks1 < baris &
       indeks2 >= 0 & indeks2 < kolom)
      return true;
     
    return false;
  }
}

// Demonstrasi indekser 2D.
class DemoDuaDIndekser {
  static void Main() {
    Array2DGagalHalus fs = new Array2DGagalHalus(3, 5);
    int x;

    // Menampilkan kegagalan diam.
    Console.WriteLine("Gagal secara diam-diam.");

    for(int i=0; i < 6; i++)
      fs[i, i] = i*10;

    for(int i=0; i < 6; i++) {
      x = fs[i,i];

      if(x != -1) Console.Write(x + " ");
    }

    Console.WriteLine();
 
    // Sekarang, tampilkan kegagalan.
    Console.WriteLine("\nKegagalan dengan laporan error.");
    for (int i = 0; i < 6; i++)
    {
      fs[i, i] = i * 10;
      if (fs.ErrFlag)
        Console.WriteLine("fs[" + i + ", " + i + "] di luar batas error");
    }

    for (int i = 0; i < 6; i++)
    {
      x = fs[i, i];
      if (!fs.ErrFlag) Console.Write(x + " ");
      else
        Console.WriteLine("fs[" + i + ", " + i + "] di luar batas error");
    }
  }
}

Keluaran program yang dihasilkan ditampilkan di sini:

Gagal secara diam-diam.
0 10 20 0 0 0

Kegagalan dengan laporan error.
fs[3, 3] di luar batas error
fs[4, 4] di luar batas error
fs[5, 5] di luar batas error
0 10 20 fs[3, 3] di luar batas error
fs[4, 4] di luar batas error
fs[5, 5] di luar batas error


Properti
Tipe lain dari anggota kelas adalah properti. Aturan umumnya adalah bahwa sebuah properti menggabungkan bidang dengan metode yang mengaksesnya. Anda mungkin seringkali ingin menciptakan sebuah bidang yang tersedia bagi pengguna sebuah objek, tetapi Anda juga ingin mengendalikan operasi apa saja yang boleh diterapkan pada bidang tersebut. Misalnya, Anda ingin membatasi rentang nilai yang dapat ditugaskan kepada bidang tersebut. Meskipun dimungkinkan untuk melakukan tujuan ini dengan memanfaatkan variabel privat dengan metode yang bisa mengaksesnya, properti menawarkan pendekatan yang lebih baik.

Properti sama dengan indekser. Properti memuat sebuah nama dengan aksesor get dan set. Aksesor tersebut dipakai untuk mendapatkan dan menetapkan nilai sebuah variabel. Keuntungan kunci dari properti adalah bahwa namanya dapat dipakai di dalam ekspresi dan penugasan seperti halnya variabel biasa.  Pada kasus tersebut, aksesor get dan set dipanggil secara otomatis.

Bentuk umum dari sebuah properti ditunjukkan di sini:

tipe nama {
  get {
    // kode aksesor get
  }

  set {
    // kode aksesor set
  }
}
Di sini, tipe menspesifikasi tipe properti, seperti int, dan nama adalah nama dari properti. Begitu properti didefinisikan, sembarang penggunaan dari nama akan menghasilkan pemanggilan terhadap aksesornya. Aksesor set secara otomatis menerima parameter, yang dinamakan value, yang memuat nilai yang sedang ditugaskan kepada properti.

Penting untuk memahami bahwa properti tidak mendefinisikan lokasi penyimpanan. Properti secara umum mengelola akses ke suatu bidang. Ia sendiri tidak menyediakan bidang tersebut. Bidang harus dispesifikasi secara independen dari properti.

Berikut adalah sebuah contoh yang mendemonstrasikan sebuah properti, yang dinamakan PropKu, yang digunakan untuk mengakses bidang prop. Pada kasus ini, properti hanya membolehkan penugasan nilai positif.

// Contoh sederhana properti.
using System;

class PropSederhana
{
    int prop; // bidang yang dikelola oleh PropKu
    public PropSederhana() { prop = 0; }
   
    /* Ini adalah properti yang mendukung akses terhadap
       variabel instans private prop. Ia hanya membolehkan
       penugasan nilai positif. */
    public int PropKu
    {
        get
        {
            return prop;
        }
       
        set
        {
            if (value >= 0) prop = value;
        }
    }
}

// Demonstrasi sebuah properti.
class DemoProperti
{
    static void Main()
    {
        PropSederhana ob = new PropSederhana();

        Console.WriteLine("Nilai asli dari ob.PropKu: " + ob.PropKu);

        ob.PropKu = 100; // menugaskan nilai
        Console.WriteLine("Nilai dari ob.PropKu: " + ob.PropKu);
       
        // Tidak bisa menugaskan nilai negatif kepada prop.
        Console.WriteLine("Mencoba menugaskan -10 kepada ob.PropKu");
        ob.PropKu = -10;
        Console.WriteLine("Nilai dari ob.PropKu: " + ob.PropKu);
    }
}


Keluaran program ditampilkan di sini:

Nilai asli dari ob.PropKu: 0
Nilai dari ob.PropKu: 100
Mencoba menugaskan -10 kepada ob.PropKu
Nilai dari ob.PropKu: 100

Akan dibahas program ini dengan hati-hati. Program mendefinisikan satu bidang privat, yang dinamakan prop, dan sebuah properti, yang dinamakan PropKu, yang mengelola akses terhadap prop. Seperti dijelaskan sebelumnya, properti sendiri tidak mendefinisikan lokasi penyimpanan, properti hanya mengelola akses terhadap sebuah bidang. Di samping itu, karena prop dideklarasikan private, ia hanya bisa diakses melalui PropKu.

Properti PropKu dispesifikasi sebagai public sehingga ia dapat diakses oleh kode di luar kelasnya. Ini masuk akal karena ia menyediakan akses terhadap prop, yang dideklarasikan private. Aksesor get hanya menjadikan nilai prop sebagai nilai balik. Aksesor set menetapkan nilai prop jika dan hanya jika nilai tersebut positif. Jadi, properti PropKu mengendalikan nilai yang bisa ditugaskan kepada prop. Ini merupakan esensi dari properti.

TIpe properti yang didefinisikan oleh PropKu disebut dengan poperti read-write karena ia mengijinkan bidang untuk dibaca dan ditulis. Untuk menciptakan properti read-only, Anda hanya perlu mendefinisikan aksesor get. Untuk menciptakan properti write-only, Anda hanya perlu mendefinisikan aksesor set.

Anda dapat menggunakan sebuah properti untuk memperbaiki array kelas gagal-halus. Seperti Anda ketahui, semua array mempunyai properti Length. Sampai saat ini, kelas ArrayGagalHalus hanya menggunakan sebuah bidang integer publik, yang dinamakan Panjang untuk tujuan ini. Hal itu tidak direkomendasikan, karena bisa saja Panjang ditetapkan tidak sama dengan panjang array terkait. Situasi ini akan diperbaiki dengan memanfaatkan Length, sebuah bidang read-only, seperti ditunjukkan pada versi ArrayGagalHalus berikut:

// Menambahkan properti Length pada ArrayGagalHalus.
using System;

class ArrayGagalHalus
{
    int[] a; // referensi ke array
    int Panjang; // Panjang array
   
    public bool ErrFlag; // mengindikasikan keluaran operasi terakhir

    // Mengkonstruksi array dengan ukuran tertentu.
    public ArrayGagalHalus(int ukuran)
    {
        a = new int[ukuran];
        Panjang = ukuran;
    }

    // Properti Length read-only.
    public int Length
    {
        get
        {
            return Panjang;
        }
    }

    // Ini adalah indekser untuk ArrayGagalHalus.
    public int this[int indeks]
    {
        // Ini adalah aksesor get.
        get
        {
            if (ok(indeks))
            {
                ErrFlag = false;
                return a[indeks];
            }
           
            else
            {
                ErrFlag = true;
                return 0;
            }
        }

        // Ini adalah aksesor set.
        set
        {
            if (ok(indeks))
            {
                a[indeks] = value;
                ErrFlag = false;
            }
            else ErrFlag = true;
        }
    }

    // Menghasilkan true jika indeks di dalam batas.
    private bool ok(int indeks)
    {
        if (indeks >= 0 & indeks < Length) return true;
        return false;
    }
}

// Mendemonstrasikan array gagal-halus terperbaiki
class DemoGHTerperbaiki
{
    static void Main()
    {
        ArrayGagalHalus fs = new ArrayGagalHalus(5);
        int x;

        // Dapat membaca Length.
        for (int i = 0; i < fs.Length; i++)
            fs[i] = i * 10;

        for (int i = 0; i < fs.Length; i++)
        {
            x = fs[i];
            if (x != -1) Console.Write(x + " ");
        }
        Console.WriteLine();

        // fs.Length = 10; // Error, ilegal!
    }
}

Length sekarang adalah properti yang menggunakan variabel private, Panjang, untuk penyimpanannya. Length hanya mendefinisikan aksesor get, yang berarti ia adalah properti read-only. Jadi, Length hanya bisa dibaca, dan tidak bisa diubah. Untuk membuktikannya, Anda bisa menghapus simbol komentar yang ada di awal baris ini:

// fs.Length = 10; // Error, ilegal!

Ketika Anda mencoba mengkompilasinya, Anda akan menerima pesan error yang menyatakan bahwa Length adalah read-only. Meskipun penambahan properti Length memperbaiki ArrayGagalHalus, itu bukan satu-satunya perbaikan yang bisa dilakukan menggunakan properti. Anggota ErrFlag juga merupakan kandidat utama yang bisa diubah menjadi properti karena akses terhadapnya harus dibatasi hanya bersifat read-only. Berikut adalah perbaikan akhir atas ArrayGagalHalus, yang menciptakan sebuah properti, bernama Error, yang menggunakan variabel asli ErrFlag sebagai penyimpananya.

// Menambahkan properti Error pada ArrayGagalHalus.
using System;

class ArrayGagalHalus
{
    int[] a; // referensi ke array
    int Panjang; // Panjang array
   
    public bool ErrFlag; // mengindikasikan keluaran operasi terakhir

    // Mengkonstruksi array dengan ukuran tertentu.
    public ArrayGagalHalus(int ukuran)
    {
        a = new int[ukuran];
        Panjang = ukuran;
    }

    // Properti Length read-only.
    public int Length
    {
        get
        {
            return Panjang;
        }
    }

    // Properti Error read-only.
    public bool Error
    {
        get
        {
            return ErrFlag;
        }
    }

    // Ini adalah indekser untuk ArrayGagalHalus.
    public int this[int indeks]
    {
        // Ini adalah aksesor get.
        get
        {
            if (ok(indeks))
            {
                ErrFlag = false;
                return a[indeks];
            }
           
            else
            {
                ErrFlag = true;
                return 0;
            }
        }

        // Ini adalah aksesor set.
        set
        {
            if (ok(indeks))
            {
                a[indeks] = value;
                ErrFlag = false;
            }
           
            else ErrFlag = true;
        }
    }

    // Menghasilkan true jika indeks di dalam batas.
    private bool ok(int indeks)
    {
        if (indeks >= 0 & indeks < Length) return true;
        return false;
    }
}

// Mendemonstrasikan array gagal-halus terperbaiki
class DemoGHAkhir
{
    static void Main()
    {
        ArrayGagalHalus fs = new ArrayGagalHalus(5);

        // Menggunakan properti Error.
        for (int i = 0; i < fs.Length + 1; i++)
        {
            fs[i] = i * 10;
            if (fs.Error)
                Console.WriteLine("Error dengan indeks " + i);
        }
    }
}


Menggunakan Penginisialisasi Objek Dengan Properti
Seperti didiskusikan pada Bab 7, penginisialisasi objek menyediakan cara alternatif dalam memanggil secara eksplisit sebuah konstruktor ketika penciptaan objek dilakukan. Ketika menggunakan penginisialisasi objek, Anda harus menspesifikasi nilai awal untuk bidang dan/atau properti yang ingin diinisialisasi. Di samping itu, sintaks penginisialisasi objek sama untuk bidang maupun properti. Sebagai contoh, di sini didemonstrasikan penginisialisasi objek dari Bab 7, dikerjakan ulang untuk menunjukkan kegunaan penginisialisasi objek dengan properti. Ingat bahwa versi pada Bab 7 menggunakan bidang. Satu-satunya perbedaan antara versi ini dengan yang ada pada Bab 7 adalah bahwa Hitung dan Str dikonversi dari bidang menjadi properti. Sintaks penginisialisasi objek tidak berubah.

// Menggunakan penginisialisasi objek dengan properti.
using System;

class KelasKu
{
    // Sekarang ini properti.
    public int Hitung { get; set; }
    public string Str { get; set; }
}

class DemoInitObjek
{
    static void Main()
    {
        // Menciptakan sebuah objek KelasKu menggunakan penginisialisasi objek.
        KelasKu obj = new KelasKu { Hitung = 100, Str = "Pengujian" };
        Console.WriteLine(obj.Hitung + " " + obj.Str);
    }
}

Seperti yang dapat Anda lihat, properti Hitung dan Str ditetapkan melalui ekspresi penginisialisasi objek. Keluarannya sama dengan yang dihasilkan pada Bab 7 dan ditampilkan kembali di sini:

110 Pengujian


Menggunakan Pemodifikasi Akses Dengan Aksesor
Secara default, aksesor set dan get memiliki aksesibilitas sebagai indekser atau properti. Sebagai contoh, jika properti dideklarasikan public, maka secara default aksesor set dan get juga public. Dimungkinkan untuk memberikan pemodifikasi akses private bagi set dan get.

Ada sejumlah alasan mengapa Anda ingin membatasi aksesibilitas suatu aksesor. Sebagai contoh, Anda mungkin ingin membiarkan semua orang bisa memperoleh nilai sebuah properti, tetapi hanya mengijinkan anggota kelasnya saja untuk menetapkan nilai properti. Untuk melakukannya, aksesor set dideklarasikan sebagai private. Sebagai contoh, berikut adalah sebuah properti, bernama PropKu, yang mempunyai aksesor set dispesifikasi sebagai private.

// Menggunakan pemodifikasi akses dengan aksesor.
using System;

class AksesProp {
  int prop; // bidang yang sedang dikelola oleh PropKu

  public AksesProp() { prop = 0; }

  /* Ini adalah properti yang mendukung akses terhadap
     variabel instans private prop. Properti ini mengijinkan
     sembarang kode untuk mendapatkan nilai prop, tetapi hanya
     anggota kelasnya yang bisa menetapkan nilai prop. */

  public int PropKu {
    get {
      return prop;
    }

    private set { // sekarang, private
      prop = value;
    }
  }

  // Anggota kelas ini menginkremen nilai dari PropKu.
  public void InkrProp()
  {
    PropKu++; // OK, dalam sesama kelas.
  }
}

// Demonstrasi pemodifikasi akses aksesor.
class DemoAksesProp
{
    static void Main()
    {
        AksesProp ob = new AksesProp();

        Console.WriteLine("Nilai asli dari ob.PropKu: " + ob.PropKu);

        // ob.PropKu = 100; // tidak dapat mengakses set

        ob.InkrProp();
        Console.WriteLine("Nilai dari ob.PropKu setelah inkremen: "
                          + ob.PropKu);
    }
}

Keluaran program ditampilkan di sini:

Nilai asli dari ob.PropKu: 0
Nilai dari ob.PropKu setelah inkremen: 1

Pada kelas AksesPro, aksesor set dispesifikasi private. Ini berarti bahwa ia dapat diakses hanya oleh anggota kelas, seperti InkrProp(), tetapi tidak bisa diakses oleh kode di luar AksesPro. Inilah mengapa percobaan untuk menugaskan sebuah nilai kepada ob.PropKu di dalam DemoAksesProp tidak bisa dilakukan.

Berikut disajikan satu lagi contoh, dimana aksesor set pada kedua properti Length dan Error dideklarasikan private:

// Menambahkan properti Length dan Error read-only pada ArrayGagalHalus.
using System;

class ArrayGagalHalus
{
    int[] a; // referensi ke array
    int Panjang; // Panjang array

    // Mengkonstruksi array dengan ukuran tertentu.
    public ArrayGagalHalus(int ukuran)
    {
        a = new int[ukuran];
        Panjang = ukuran;
    }

    // sebuah properti read-only, implementasi-diri, Length.
    public int Length { get; private set; }

    // sebuah properti read-only, implementasi-diri, Error.
    public bool Error { get; private set; }

     // Ini adalah indekser untuk ArrayGagalHalus.
    public int this[int indeks]
    {
        // Ini adalah aksesor get.
        get
        {
            if (ok(indeks))
            {
                Error = false;
                return a[indeks];
            }

            else
            {
                Error = true;
                return 0;
            }
        }

        // Ini adalah aksesor set.
        set
        {
            if (ok(indeks))
            {
                a[indeks] = value;
                Error = false;
            }

            else Error = true;
        }
    }

    // Menghasilkan true jika indeks di dalam batas.
    private bool ok(int indeks)
    {
        if (indeks >= 0 & indeks < Length) return true;
        return false;
    }
}

// Mendemonstrasikan array gagal-halus terperbaiki
class DemoGHAkhir
{
    static void Main()
    {
        ArrayGagalHalus fs = new ArrayGagalHalus(5);

        // Menggunakan properti Error.
        for (int i = 0; i < fs.Length + 1; i++)
        {
            fs[i] = i * 10;
            if (fs.Error)
                Console.WriteLine("Error dengan indeks " + i);
        }
    }
}


Menggunakan Indekser Dan Properti
Meskipun beberapa contoh terdahulu telah mendemonstrasikan mekanisme dasar dari indekser dan properti, kekuatan penuh dari keduanya belum ditunjukkan. Untuk menyimpulkan bab ini, sebuah kelas, RentangArray, dikembangkan yang menggunakan indekser dan properti untuk menciptakan sebuah tipe array dimana di dalamnya rentang indeks ditentukan oleh programer.

Seperti Anda ketahui, dalam C# semua array berindeks mulai dari nol. Namun, pada beberapa aplikasi, diubutuhkan sebuah array yang membolehkan pengindeksan mulai dari titik sembarang. Sebagai contoh, pada beberapa situasi diinginkan sebuah array dengan indeks mulai dari 1. Dalam situasi lain, dibutuhkan sebuah array dengan indeks negatif, misalnya dari -5 sampai 5. Kelas RentangArray yang dikembangkan di sini membolehkan pengindeksan seperti ini.

Dengan menggunakan RentangArray, Anda dapat menuliskan kode semacam ini:

RentangArray ra = new RentangArray (-5, 10);  // indeks array dari -5 sampai 10

for(int i=-5; i <= 10; i++) ra[i] = i; // indeks dari -5 sampai 10

Seperti Anda tebak, baris pertama mengkonstruksi sebuah objek RentangArray dengan indeks -5 sampai 10. Argumen pertama menspesifikasi indeks awal. Argumen kedua menspesifikasi indeks akhir. Begitu ra dikonstruksi, ia dapat diindeks dari -5 sampai 10.

Kelas RentangArray ditampilkan di sini, bersama dengan DemoRentangArray, yang mendemonstrasikan array. Seperti diimplementasikan di sini, RentangArray mendukung array int, tetapi Anda dapat mengganti tipe datanya, jika diperlukan.

/* Menciptakan kelas rentang array.
   Kelas RentangArray membolehkan pengindeksan dimulai dari
   nilai tertentu selain 0. Ketika Anda menciptakan sebuah RentangArray,
   Anda perlu menspesifikasi indeks awal dan indeks akhir. Indeks
   negatif juga bisa digunakan. Sebagai contoh, Anda dapat menciptakan
   array dengan indeks mulai dari -5 sampai 5, 1 sampai 10, atau 50 sampai 56.
*/
using System;

class RentangArray {
  // data private.
  int[] a; // referensi ke array

  int batasBawah; // indeks terkecil
  int batasAtas; // indeks terbesar

  // properti Length, read-only.
  public int Length { get; private set; }

  // properti Error, read-only.
  public bool Error { get; private set; }

  // menciptakan array dengan ukuran tertentu.
  public RentangArray(int bawah, int atas)
  {
    atas++;
    if (atas <= bawah)
    {
        Console.WriteLine("Indeks tak-valid");
        atas = 1; // menciptakan array minimal
        bawah = 0;
    }
   
    a = new int[atas - bawah];
    Length = atas - bawah;
    batasBawah = bawah;
    batasAtas = --atas;
  }

  // Ini adalah indekser untuk RentangArray.
  public int this[int indeks]
  {
    // Ini adalah aksesor get.
    get
    {
        if (ok(indeks))
        {
            Error = false;
            return a[indeks - batasBawah];
        }
       
        else
        {
            Error = true;
            return 0;
        }
    }
   
    // Ini adalah aksesor set.
    set
    {
        if (ok(indeks))
        {
            a[indeks - batasBawah] = value;
            Error = false;
        }
        else Error = true;
    }
  }

  // Menghasilkan true jika indeks di dalam batas.
  private bool ok(int indeks)
  {
    if (indeks >= batasBawah & indeks <= batasAtas) return true;
    return false;
  }
}

// Demonstrasi array rentang-indeks.
class DemoRentangArray
{
    static void Main()
    {
        RentangArray ra = new RentangArray(-5, 5);
        RentangArray ra2 = new RentangArray(1, 10);
        RentangArray ra3 = new RentangArray(-20, -12);
       
        // Demonstrasi ra.
        Console.WriteLine("Panjang dari ra: " + ra.Length);
       
        for (int i = -5; i <= 5; i++)
            ra[i] = i;
       
        Console.Write("Isi dari ra: ");
        for (int i = -5; i <= 5; i++)
            Console.Write(ra[i] + " ");
       
        Console.WriteLine("\n");

        // Demonstrasi ra2.
        Console.WriteLine("Panjang dari ra2: " + ra2.Length);
       
        for (int i = 1; i <= 10; i++)
            ra2[i] = i;
       
        Console.Write("Isi dari ra2: ");
       
        for (int i = 1; i <= 10; i++)
            Console.Write(ra2[i] + " ");
       
        Console.WriteLine("\n");

        // Demonstrasi ra3.
        Console.WriteLine("Panjang dari ra3: " + ra3.Length);
       
        for (int i = -20; i <= -12; i++)
            ra3[i] = i;
       
        Console.Write("Panjang dari ra3: ");
       
        for (int i = -20; i <= -12; i++)
            Console.Write(ra3[i] + " ");
       
        Console.WriteLine("\n");
    }
}

Keluaran program ditampilkan di sini:

Panjang dari ra: 11
Isi dari ra: -5 -4 -3 -2 -1 0 1 2 3 4 5

Panjang dari ra2: 10
Isi dari ra2: 1 2 3 4 5 6 7 8 9 10

Panjang dari ra3: 9
Panjang dari ra3: -20 -19 -18 -17 -16 -15 -14 -13 -12

Seperti ditegaskan keluran, objek bertipe RentangArray dapat diindeks bukan hanya dari nol. Akan ditengok lebih dekat tentang bagaimana RentangArray diimplementasikan.

RentangArray dimulai dengan pendefinisan beberapa variabel instans:

// data private.
int[] a; // referensi ke array
int batasBawah; // indeks terkecil
int batasAtas; // indeks terbesar

Array untuk penyimpanan ditunjuk oleh a. Array ini dialokasikan oleh konstruktor RentangArray. Indeks dari batas bawah array disimpan di dalam batasBawah, dan indeks dari batas atas array disimpan di dalam batasAtas.

Selanjutnya, properti read-only Length dan Error dideklarasikan:

// properti Length, read-only.
public int Length { get; private set; }

// properti Error, read-only.
public bool Error { get; private set; }

Perhatikan bahwa untuk kedua properti, aksesor set dideklarasikan private. Seperti dijelaskan sebelumnya pada bab ini, ini menghasilkan properti read-only.

Konstruktor RentangArray ditampilkan di sini:

// menciptakan array dengan ukuran tertentu.
public RentangArray(int bawah, int atas)
{
  atas++;
  if (atas <= bawah)
  {
    Console.WriteLine("Indeks tak-valid");
    atas = 1; // menciptakan array minimal
    bawah = 0;
  }
   
  a = new int[atas - bawah];
  Length = atas - bawah;
  batasBawah = bawah;
  batasAtas = --atas;
}

RentangArray dikonstruksi dengan melewatkan indeks batas bawah di dalam bawah dan indeks batas atas di dalam atas. Nilai dari atas kemudian diinkremen karena indeks yang dispesifikasi inklusif. Selanjutnya, pemeriksaan dilakukan untuk memastikan bahwa indeks atas lebih besar dari indeks bawah. Jika tidak, error akan dilaporkan dan sebuah array satu-elemen diciptakan. Berikutnya, penyimpanan untuk array dialokasikan dan ditugaskan kepada a. Kemudian properti Length ditetapkan sama dengan jumlah elemen di dalam array. Terakhir, batasBawah dan batasAtas ditetapkan.

Berikutnya, RentangArray mengimplementasikan indeksernya, seperti ditunjukkan di sini:

// Ini adalah indekser untuk RentangArray.
public int this[int indeks]
{
  // Ini adalah aksesor get.
  get
  {
      if (ok(indeks))
      {
          Error = false;
          return a[indeks - batasBawah];
      }
      
      else
      {
          Error = true;
          return 0;
      }
  }
   
  // Ini adalah aksesor set.
  set
  {
      if (ok(indeks))
      {
          a[indeks - batasBawah] = value;
          Error = false;
      }
      else Error = true;
  }
}

Indekser ini sama dengan yang digunakan oleh ArrayGagalHalus, dengan satu pengecualian. Perhatikan ekspresi yang mengindeks a berikut:

indeks - batasBawah

Ekspresi ini mentransformasikan indeks yang dilewatkan di dalam indeks ke dalam indeks berbasis-nol. Ekspresi ini berkerja manakala batasBawah positi, negatif, atau nol.

Metode ok() ditampilkan di sini:

The ok( ) method is shown here:

private bool ok(int indeks)
{
  if (indeks >= batasBawah & indeks <= batasAtas) return true;
  return false;
}


Ini sama dengan yang digunakan oleh ArrayGagalHalus kecuali bahwa rentang diperiksa dengan mengujinya terhadap nilai-nilai di dalam batasBawah dan batasAtas.

No comments:

Post a Comment