Capek nulis boilerplate di C#? Saatnya kita pake Record, auto rapi!

Share
Capek nulis boilerplate di C#? Saatnya kita pake Record, auto rapi!
Photo by Job Ferrari/Unsplash

Capek banget ya nulis kode yang itu itu aja buat data class di C sharp? Sumpah deh, kita semua pernah di posisi itu! Nggak cuma capek, tapi juga rawan salah karena harus ngurusin Equals, GetHashCode, ToString, dan seabrek ritual lainnya. Bikin pusing, kan? Nah, kalau kamu udah di titik ini, saatnya kita kenalan sama fitur C sharp yang keren abis, namanya record. Fitur ini bakalan bikin ngoding kita auto rapi, minim boilerplate, dan pastinya jauh lebih asik! Yuk, kita bedah tuntas kenapa record ini wajib banget kita adopsi di project project C sharp kita.

Bolak balik nulis boilerplate Aduh jangan gitu dong!

Coba deh kita ingat ingat lagi, gimana biasanya kita bikin kelas buat nyimpen data sederhana? Misalnya, data profil pengguna atau barang di toko online. Kita pasti bikin kelas kayak gini kan ya

csharp
public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }public Person(string firstName, string lastName, int age)
    {
        FirstName = firstName;
        LastName = lastName;
        Age = age;
    }// Biasanya kita juga harus override Equals dan GetHashCode
    // agar dua objek dianggap sama kalau propertinya sama
    public override bool Equals(object obj)
    {
        if (obj == null || GetType() != obj.GetType())
        {
            return false;
        }Person other = (Person)obj;
        return FirstName == other.FirstName &&
               LastName == other.LastName &&
               Age == other.Age;
    }public override int GetHashCode()
    {
        // Kombinasi hash code dari semua properti
        return HashCode.Combine(FirstName, LastName, Age);
    }// Biar kalau di debug atau di log outputnya lebih informatif
    public override string ToString()
    {
        return $"Person {{ FirstName = {FirstName}, LastName = {LastName}, Age = {Age} }}";
    }// Mungkin juga perlu operator == dan !=
    public static bool operator ==(Person left, Person right)
    {
        return EqualityComparer.Default.Equals(left, right);
    }

Lihat deh, cuma buat kelas Person yang sederhana aja, kita udah nulis banyak banget kode. Ada constructor, tiga properti, terus wajib banget override Equals, GetHashCode, ToString, bahkan operator == dan !=. Ini semua kita sebut boilerplate code. Kode kode yang harus kita tulis berulang ulang padahal fungsinya mirip mirip atau bahkan sama persis di setiap data class. Nggak cuma makan waktu, tapi juga rentan error kalau kita lupa salah satu bagiannya, atau kalau ada perubahan properti, kita harus update di banyak tempat sekaligus. Ribet kan? Nah, di sinilah record hadir sebagai pahlawan super kita.

Kenalan sama Record Si Penyelamat Kode Kita

Oke, jadi record itu apa sih? Singkatnya, record adalah jenis referensi atau jenis nilai yang tujuannya utama adalah untuk menyimpan data. record ini diperkenalkan di C sharp versi sembilan dan dirancang khusus buat data class. Dia ngasih kita kelebihan bawaan yang secara otomatis nanganin banyak boilerplate yang tadi kita sebutkan.

Dengan record, kode Person kita yang tadi bisa jadi super singkat kayak gini

csharp
public record Person(string FirstName, string LastName, int Age);

Gila, cuma satu baris! Dari puluhan baris kode jadi satu baris doang. Ini bukan sihir, tapi memang kecanggihan record. Dia secara otomatis nge generate Equals, GetHashCode, ToString, bahkan constructor dan deconstructor buat kita. Mantap banget kan?

Tapi inget ya, meskipun sintaksnya super ringkas dan mirip struct yang biasanya dipakai buat value type, secara default record itu adalah record class, artinya dia tetap reference type sama seperti class biasa. Bedanya, dia punya value based equality secara bawaan. Artinya, dua record dianggap sama jika semua propertinya punya nilai yang sama, bukan hanya referensi memori yang sama. Ini beda banget sama class biasa yang secara default ngebandingin referensi memori.

Kok bisa sih Record auto rapi gitu Fitur Intinya Apa Aja

record itu punya beberapa fitur bawaan yang bikin hidup kita sebagai developer C sharp jadi lebih mudah dan kodingan lebih rapi. Yuk, kita kupas satu per satu.

Value based equality

Ini salah satu fitur paling powerful dari record. Ketika kita ngebandingin dua objek record, C sharp otomatis ngebandingin nilai dari semua propertinya. Jadi, kita nggak perlu repot repot override Equals dan GetHashCode lagi. record udah ngurusin itu buat kita.

Coba kita lihat contohnya

csharp
public record Person(string FirstName, string LastName, int Age);// ... di dalam method main atau lainnya
var person1 = new Person("Budi", "Santoso", 30);
var person2 = new Person("Budi", "Santoso", 30);
var person3 = new Person("Ani", "Pratiwi", 25);

Kalau kita pake class biasa tanpa override Equals, person1 == person2 pasti bakal false karena mereka adalah dua objek berbeda di memori. Tapi dengan record, hasilnya True karena record membandingkan nilai properti mereka. Ini asik banget buat nanganin value objects atau DTOs (Data Transfer Objects) di mana kita peduli dengan isinya, bukan identitas objeknya di memori.

Immutability by default atau bawaan

Seringkali, data class kita inginnya bersifat immutable atau tidak bisa diubah setelah dibuat. Ini bagus buat mencegah bug, mempermudah concurrency, dan membuat kode lebih predictable. Nah, record ini punya kecenderungan ke immutability.

Ketika kita pake record dengan positional syntax seperti public record Person(string FirstName, string LastName, int Age);, properti properti yang didefinisikan di situ secara otomatis punya init only setter. Artinya, properti itu cuma bisa di set pas inisialisasi objek, setelah itu nggak bisa diubah.

Contoh

csharp
public record Product(int Id, string Name, decimal Price);

Kalau kita ingin properti bisa diubah, kita bisa pakai sintaks record yang lebih mirip class biasa atau yang sering disebut nominal record, dan kita bisa pakai set alih alih init. Tapi, kebanyakan kasus untuk data, init lebih disukai.

ToString Otomatis yang informatif

Berapa kali kita harus nulis method ToString() manual cuma buat dapetin output yang representatif pas debugging atau logging? Sering banget kan? record juga ngurusin ini. Dia secara otomatis nge generate method ToString() yang nyetak nama record dan semua propertinya beserta nilainya.

Contoh

csharp
public record Book(string Title, string Author, int Year);

Super rapi dan nggak perlu effort tambahan dari kita. Asik banget!

With expressions untuk non destructive mutation

Meskipun record cenderung immutable, bukan berarti kita nggak bisa "mengubah" datanya. Kita bisa membuat salinan objek record dengan beberapa properti yang diubah. Ini disebut non destructive mutation dan dilakukan menggunakan with expressions. Fitur ini keren banget buat skenario di mana kita perlu membuat versi baru dari objek data tanpa memodifikasi objek aslinya.

Contoh

csharp
public record Customer(int Id, string Name, string City);var originalCustomer = new Customer(1, "Adi", "Bandung");
Console.WriteLine($"Original: {originalCustomer}"); // Customer { Id = 1, Name = Adi, City = Bandung }// Buat salinan dengan nama dan kota yang berbeda
var updatedCustomer = originalCustomer with { Name = "Aditya", City = "Jakarta" };
Console.WriteLine($"Updated: {updatedCustomer}");   // Customer { Id = 1, Name = Aditya, City = Jakarta }

with expressions ini memastikan objek originalCustomer tidak berubah. Kita hanya membuat objek record baru (updatedCustomer) berdasarkan originalCustomer dengan beberapa modifikasi. Ini adalah pattern yang sangat kuat dalam pengembangan aplikasi modern, terutama di aplikasi yang menggunakan arsitektur immutable data atau functional programming.

Positional records vs Nominal records

Kita sudah lihat contoh record dengan sintaks paling ringkas yaitu positional record. Tapi, record juga bisa dideklarasikan dengan cara yang lebih mirip class biasa, yang kita sebut nominal record.

Positional records

public record Person(string FirstName, string LastName, int Age);

  • Paling ringkas
  • Otomatis nge generate constructor utama, properti dengan init setter, dan deconstructor.
  • Ideal buat data yang simpel.

Nominal records

public record Item { public int Id { get; init; } public string Name { get; init; } public decimal Price { get; init; } }

  • Mirip class biasa, tapi tetap punya fitur record seperti Equals, GetHashCode, ToString, dan with expressions.
  • Kita bisa punya constructor tambahan, properti set biasa (walau jarang direkomendasikan untuk record), atau field biasa.
  • Lebih fleksibel kalau kita perlu properti yang nggak langsung di set lewat constructor atau punya logika inisialisasi yang lebih kompleks.

Pilihan antara positional atau nominal tergantung kebutuhan. Untuk data yang sangat sederhana dan ingin immutable, positional record adalah pilihan terbaik. Untuk data yang lebih kompleks atau memerlukan fleksibilitas lebih dalam deklarasi properti, nominal record bisa jadi solusi.

Record di Dunia Nyata Kapan Kita Pake

record ini bukan cuma teori, tapi punya banyak kegunaan praktis di project kita.

Data Transfer Objects DTOs

Ini adalah salah satu skenario paling umum. DTOs adalah objek yang tugasnya cuma buat ngirim data antar layer aplikasi atau antar sistem. Misalnya, dari API ke frontend, atau dari database ke service layer. Dengan record, DTO kita jadi super ringkas, jelas, dan aman karena cenderung immutable. Kita nggak perlu kuatir data DTO berubah secara tidak sengaja di tengah jalan.

csharp
public record UserDto(int Id, string Username, string Email);
public record ProductDto(int ProductId, string ProductName, decimal UnitPrice);

Immutable messages

Dalam arsitektur berbasis pesan seperti event driven architecture atau command query responsibility segregation (CQRS), kita sering banget ngirim pesan atau event. Pesan pesan ini harus immutable setelah dibuat, biar nggak ada yang merubah isinya secara sembunyi sembunyi. record adalah pilihan sempurna buat ini.

csharp
public record OrderPlacedEvent(Guid OrderId, DateTime Timestamp, decimal TotalAmount);
public record CreateProductCommand(string Name, string Description, decimal Price);

Value objects

Value objects adalah objek yang identitasnya ditentukan oleh nilai propertinya, bukan oleh identitas referensi. Contoh klasik adalah Money, Address, atau DateRange. Dua objek Money yang punya nilai mata uang dan jumlah yang sama dianggap sama. record dengan value based equality-nya sangat pas buat implementasi value objects.

csharp
public record Address(string Street, string City, string PostalCode);
public record Money(decimal Amount, string Currency);

Konfigurasi aplikasi

Pengaturan atau konfigurasi aplikasi juga seringkali bersifat immutable setelah aplikasi startup. Menggunakan record untuk menyimpan data konfigurasi bisa jadi pilihan yang cerdas.

Tapi Record itu Class atau Struct Hmm Pilihan Kita Gimana

Tadi kita sempat singgung kalau record itu secara default adalah record class. Tapi, ada juga record struct yang diperkenalkan di C sharp versi sepuluh. Keduanya punya tujuan yang sama yaitu menyediakan fitur record (value based equality, ToString otomatis, with expressions) tapi dengan karakteristik tipe yang berbeda.

Record Class (default)

record class adalah reference type. Dia di alokasikan di heap memory dan referensinya yang disimpan.

  • Kapan pakai record class?

* Untuk objek yang ukurannya relatif besar. Ketika kita sering meneruskan objek ke method lain dan ingin menghindari copying* data yang mahal. Ketika kita butuh nullability*, karena record class bisa bernilai null.

Contoh deklarasi eksplisit

csharp
public record class Employee(int Id, string Name);

Record Struct

record struct adalah value type. Dia di alokasikan di stack memory (untuk variabel lokal) atau inline di dalam tipe lain.

  • Kapan pakai record struct?

* Untuk objek yang ukurannya kecil (biasanya kurang dari 16 byte). Ketika kita ingin menghindari alokasi di heap* untuk performance kritikal. Ketika value semantics* benar benar diperlukan dan objek tidak akan diubah setelah dibuat.

Contoh deklarasi

csharp
public record struct Point(int X, int Y);
public readonly record struct Dimensions(double Width, double Height); // Bisa juga readonly record struct

Penting banget buat milih antara record class dan record struct berdasarkan kebutuhan aplikasi kita, terutama soal performa dan manajemen memori. Kalau ragu, record class adalah pilihan yang aman sebagai default.

Tips Pro biar Pake Record Makin Asik

Agar pengalaman kita pakai record makin maksimal dan asik, ada beberapa tips dan best practice yang bisa kita terapkan.

Record inheritance

Ya, record bisa melakukan inheritance atau pewarisan. Sebuah record bisa mewarisi dari record lain. Ini berlaku untuk record class. record struct tidak mendukung inheritance. Ketika kita menggunakan inheritance dengan record, fitur value based equality juga akan dipertimbangkan dalam hierarki pewarisan.

csharp
public record Vehicle(string Manufacturer, string Model);
public record Car(string Manufacturer, string Model, int NumberOfDoors) : Vehicle(Manufacturer, Model);var vehicle = new Vehicle("Toyota", "Corolla");
var car = new Car("Toyota", "Corolla", 4);

Fitur ini berguna kalau kita punya hierarki data yang alami.

Mengatasi koleksi di dalam record

Kalau record kita punya properti berupa koleksi (misalnya List), perlu hati hati. Meskipun record cenderung immutable, koleksi di dalamnya mungkin tidak immutable. Kalau kita membuat record seperti ini

csharp
public record Order(int Id, List Items);

Meskipun objek Order itu sendiri immutable (kita nggak bisa ganti Id atau Items setelah inisialisasi), tapi objek List di dalamnya tetap bisa dimodifikasi (misalnya kita bisa order.Items.Add("New Item");). Untuk mencapai true immutability, kita sebaiknya menggunakan koleksi immutable seperti ImmutableList atau ImmutableArray dari namespace System.Collections.Immutable.

csharp
using System.Collections.Immutable;public record Order(int Id, ImmutableList Items);

Ini memastikan bahwa tidak ada bagian dari record yang bisa diubah setelah dibuat.

Kapan tidak menggunakan Record

Meskipun record itu asik, bukan berarti dia cocok buat semua skenario. Ada beberapa kondisi di mana class biasa mungkin lebih tepat.

  • Objek yang punya mutable state: Kalau objek kita memang dirancang untuk punya state yang sering berubah setelah dibuat (misalnya objek yang melacak session atau state UI), class biasa dengan set setter akan lebih sesuai.
  • Objek yang fokus pada behavior daripada data: Kalau objek kita lebih banyak punya method daripada properti data, atau fungsinya lebih ke perilaku (misalnya Repository atau Service), class adalah pilihan yang lebih tepat. record sangat unggul untuk objek data.

Ketika identitas referensi itu penting: Jika kita perlu membedakan dua objek meskipun isinya sama (misalnya, dua instance dari objek yang merepresentasikan sebuah koneksi database), maka class biasa dengan reference based equality* akan lebih cocok.

Record vs Class Biasa Ini Bukan Perang Tapi Pilihan Cerdas

Intinya, record itu bukan pengganti class secara keseluruhan. Dia adalah spesialisasi dari class atau struct yang dirancang untuk objek yang tugas utamanya adalah menampung data. Dengan semua fitur bawaan yang dia miliki, record secara drastis mengurangi boilerplate code dan meningkatkan kejelasan serta keamanan kode kita, terutama untuk value objects dan DTOs.

class tetap punya tempatnya sendiri di C sharp, terutama untuk objek yang punya state mutable, objek yang kaya behavior, atau objek yang identitas referensinya itu penting.

Penting bagi kita sebagai developer C sharp untuk memahami perbedaan dan kekuatan masing masing, sehingga kita bisa membuat pilihan yang cerdas kapan harus menggunakan record dan kapan harus tetap menggunakan class biasa. Ini bukan soal mana yang lebih baik secara mutlak, tapi mana yang paling tepat untuk masalah yang sedang kita hadapi.

Yuk Mulai Pake Record Sekarang Juga

Gimana, asik banget kan fitur record di C sharp ini? Nggak cuma bikin kode kita jadi lebih ringkas dan auto rapi, tapi juga meningkatkan maintainability dan readability. Kita bisa fokus ke logika bisnis inti tanpa harus pusing mikirin boilerplate code yang itu itu aja.

Kalau kamu masih nulis Equals, GetHashCode, dan ToString manual di data class kamu, saatnya beralih ke record! Mulai dari project kecil atau DTOs yang sederhana. Kamu pasti akan merasakan perbedaannya. Selamat ngoding dengan lebih efisien dan auto rapi! Jangan sungkan berbagi pengalaman kamu pakai record ya di kolom komentar.

Read more