Buang - Dasar-Dasar C#

Buang adalah variabel tempat penampung yang sengaja tidak digunakan dalam kode aplikasi. Pembuangan setara dengan variabel yang tidak ditetapkan; mereka tidak memiliki nilai. Buang mengkomunikasikan niat kepada pengompilasi dan orang lain yang membaca kode Anda: Anda bermaksud mengabaikan hasil ekspresi. Anda mungkin ingin mengabaikan hasil ekspresi, satu atau beberapa anggota ekspresi tuple, parameter out ke metode, atau target ekspresi pencocokan pola.

Membuang membuat niat kode Anda jelas. Buang menunjukkan bahwa kode kami tidak pernah menggunakan variabel. Mereka meningkatkan keterbacaan dan pemeliharaannya.

Anda menunjukkan bahwa variabel adalah buang dengan menetapkan garis bawah (_) sebagai namanya. Misalnya, panggilan metode berikut mengembalikan tuple di mana nilai pertama dan kedua dibuang. area adalah variabel yang dideklarasikan sebelumnya yang disetel ke komponen ketiga yang dikembalikan oleh GetCityInformation:

(_, _, area) = city.GetCityInformation(cityName);

Anda dapat menggunakan buang untuk menentukan parameter input yang tidak digunakan dari ekspresi lambda. Untuk informasi selengkapnya, lihat bagian Parameter input ekspresi lambda dari artikel Ekspresi Lambda.

Ketika _ adalah pembuangan yang valid, mencoba mengambil nilainya atau menggunakannya dalam operasi penugasan menghasilkan kesalahan pengkompilasi CS0103, "Nama '_' tidak ada dalam konteks saat ini". Kesalahan ini karena _ tidak diberi nilai, dan bahkan mungkin tidak ditetapkan lokasi penyimpanan. Jika itu adalah variabel aktual, Anda tidak dapat membuang lebih dari satu nilai, seperti contoh sebelumnya.

Dekonstruksi tuple dan objek

Buang berguna dalam bekerja dengan tuple ketika kode aplikasi Anda menggunakan beberapa elemen tuple tetapi mengabaikan yang lain. Misalnya, metode QueryCityDataForYears berikut mengembalikan tuple dengan nama kota, daerahnya, setahun, populasi kota untuk tahun itu, tahun kedua, dan populasi kota untuk tahun kedua tersebut. Contoh menunjukkan perubahan populasi antara dua tahun tersebut. Dari data yang tersedia dari tuple, kami tidak peduli dengan area kota, dan kami tahu nama kota dan dua tanggal pada waktu desain. Akibatnya, kami hanya tertarik pada dua nilai populasi yang disimpan dalam tuple, dan dapat menangani nilai yang tersisa sebagai buang.

var (_, _, _, pop1, _, pop2) = QueryCityDataForYears("New York City", 1960, 2010);

Console.WriteLine($"Population change, 1960 to 2010: {pop2 - pop1:N0}");

static (string, double, int, int, int, int) QueryCityDataForYears(string name, int year1, int year2)
{
    int population1 = 0, population2 = 0;
    double area = 0;

    if (name == "New York City")
    {
        area = 468.48;
        if (year1 == 1960)
        {
            population1 = 7781984;
        }
        if (year2 == 2010)
        {
            population2 = 8175133;
        }
        return (name, area, year1, population1, year2, population2);
    }

    return ("", 0, 0, 0, 0, 0);
}
// The example displays the following output:
//      Population change, 1960 to 2010: 393,149

Untuk informasi selengkapnya tentang mendekonstruksi tuple dengan buang, lihat Mendekonstruksi tuple dan jenis lainnya.

Metode Deconstruct kelas, struktur, atau antarmuka juga memungkinkan Anda untuk mengambil dan mendekonstruksi sekumpulan data tertentu dari objek. Anda dapat menggunakan buang saat Anda tertarik untuk bekerja hanya dengan subset nilai yang didekonstruksi. Contoh berikut mendekonstruksi objek Person menjadi empat string (nama depan dan belakang, kota, dan status) tetapi membuang nama belakang dan status.

using System;

namespace Discards
{
    public class Person
    {
        public string FirstName { get; set; }
        public string MiddleName { get; set; }
        public string LastName { get; set; }
        public string City { get; set; }
        public string State { get; set; }

        public Person(string fname, string mname, string lname,
                      string cityName, string stateName)
        {
            FirstName = fname;
            MiddleName = mname;
            LastName = lname;
            City = cityName;
            State = stateName;
        }

        // Return the first and last name.
        public void Deconstruct(out string fname, out string lname)
        {
            fname = FirstName;
            lname = LastName;
        }

        public void Deconstruct(out string fname, out string mname, out string lname)
        {
            fname = FirstName;
            mname = MiddleName;
            lname = LastName;
        }

        public void Deconstruct(out string fname, out string lname,
                                out string city, out string state)
        {
            fname = FirstName;
            lname = LastName;
            city = City;
            state = State;
        }
    }
    class Example
    {
        public static void Main()
        {
            var p = new Person("John", "Quincy", "Adams", "Boston", "MA");

            // Deconstruct the person object.
            var (fName, _, city, _) = p;
            Console.WriteLine($"Hello {fName} of {city}!");
            // The example displays the following output:
            //      Hello John of Boston!
        }
    }
}

Untuk informasi selengkapnya tentang mendekonstruksi jenis yang ditentukan pengguna dengan pembuangan, lihat Mendekonstruksi tuple dan jenis lainnya.

Pencocokan pola dengan switch

Pola buang dapat digunakan dalam pencocokan pola dengan ekspresi pengalihan. Setiap ekspresi, termasuk null, selalu cocok dengan pola buang.

Contoh berikut mendefinisikan metode ProvidesFormatInfo yang menggunakan ekspresi switch untuk menentukan apakah objek menyediakan implementasi IFormatProvider dan menguji apakah objek tersebut null. Ini juga menggunakan pola buang untuk menangani objek non-null dari jenis lain.

object?[] objects = [CultureInfo.CurrentCulture,
                   CultureInfo.CurrentCulture.DateTimeFormat,
                   CultureInfo.CurrentCulture.NumberFormat,
                   new ArgumentException(), null];
foreach (var obj in objects)
    ProvidesFormatInfo(obj);

static void ProvidesFormatInfo(object? obj) =>
    Console.WriteLine(obj switch
    {
        IFormatProvider fmt => $"{fmt.GetType()} object",
        null => "A null object reference: Its use could result in a NullReferenceException",
        _ => "Some object type without format information"
    });
// The example displays the following output:
//    System.Globalization.CultureInfo object
//    System.Globalization.DateTimeFormatInfo object
//    System.Globalization.NumberFormatInfo object
//    Some object type without format information
//    A null object reference: Its use could result in a NullReferenceException

Panggilan ke metode dengan parameter out

Saat memanggil metode Deconstruct untuk mendekonstruksi jenis yang ditentukan pengguna (instans kelas, struktur, atau antarmuka), Anda dapat membuang nilai argumen out individual. Namun, Anda juga dapat membuang nilai argumen out saat memanggil metode apa pun dengan parameter out.

Contoh berikut memanggil metode DateTime.TryParse(String, out TanggalWaktu) untuk menentukan apakah representasi string tanggal valid dalam budaya saat ini. Karena contoh hanya berkaitan dengan memvalidasi string tanggal dan bukan dengan mengurainya untuk mengekstrak tanggal, argumen out ke metode adalah buang.

string[] dateStrings = ["05/01/2018 14:57:32.8", "2018-05-01 14:57:32.8",
                      "2018-05-01T14:57:32.8375298-04:00", "5/01/2018",
                      "5/01/2018 14:57:32.80 -07:00",
                      "1 May 2018 2:57:32.8 PM", "16-05-2018 1:00:32 PM",
                      "Fri, 15 May 2018 20:10:57 GMT"];
foreach (string dateString in dateStrings)
{
    if (DateTime.TryParse(dateString, out _))
        Console.WriteLine($"'{dateString}': valid");
    else
        Console.WriteLine($"'{dateString}': invalid");
}
// The example displays output like the following:
//       '05/01/2018 14:57:32.8': valid
//       '2018-05-01 14:57:32.8': valid
//       '2018-05-01T14:57:32.8375298-04:00': valid
//       '5/01/2018': valid
//       '5/01/2018 14:57:32.80 -07:00': valid
//       '1 May 2018 2:57:32.8 PM': valid
//       '16-05-2018 1:00:32 PM': invalid
//       'Fri, 15 May 2018 20:10:57 GMT': invalid

Pembuangan mandiri

Anda dapat menggunakan buang mandiri untuk menunjukkan variabel apa pun yang Anda pilih untuk diabaikan. Salah satu penggunaan umumnya adalah menggunakan penugasan untuk memastikan bahwa argumen tidak null. Kode berikut menggunakan buang untuk memaksa penugasan. Sisi kanan penugasan menggunakan operator coalescing null untuk melemparkan System.ArgumentNullException ketika argumen adalah null. Kode tidak memerlukan hasil penugasan, sehingga dibuang. Ekspresi memaksa pemeriksaan null. Buang mengklarifikasi niat Anda: hasil penugasan tidak diperlukan atau digunakan.

public static void Method(string arg)
{
    _ = arg ?? throw new ArgumentNullException(paramName: nameof(arg), message: "arg can't be null");

    // Do work with arg.
}

Contoh berikut menggunakan pembuangan mandiri untuk mengabaikan objek Task yang dikembalikan oleh operasi asinkron. Menetapkan tugas memiliki efek menekan pengecualian yang dilemparkan operasi saat akan selesai. Ini membuat niat Anda jelas: Anda ingin membuang Task, dan mengabaikan kesalahan apa pun yang dihasilkan dari operasi asinkron tersebut.

private static async Task ExecuteAsyncMethods()
{
    Console.WriteLine("About to launch a task...");
    _ = Task.Run(() =>
    {
        var iterations = 0;
        for (int ctr = 0; ctr < int.MaxValue; ctr++)
            iterations++;
        Console.WriteLine("Completed looping operation...");
        throw new InvalidOperationException();
    });
    await Task.Delay(5000);
    Console.WriteLine("Exiting after 5 second delay");
}
// The example displays output like the following:
//       About to launch a task...
//       Completed looping operation...
//       Exiting after 5 second delay

Tanpa menetapkan tugas ke buang, kode berikut menghasilkan peringatan pengompilasi:

private static async Task ExecuteAsyncMethods()
{
    Console.WriteLine("About to launch a task...");
    // CS4014: Because this call is not awaited, execution of the current method continues before the call is completed.
    // Consider applying the 'await' operator to the result of the call.
    Task.Run(() =>
    {
        var iterations = 0;
        for (int ctr = 0; ctr < int.MaxValue; ctr++)
            iterations++;
        Console.WriteLine("Completed looping operation...");
        throw new InvalidOperationException();
    });
    await Task.Delay(5000);
    Console.WriteLine("Exiting after 5 second delay");

Catatan

Jika Anda menjalankan salah satu dari dua sampel sebelumnya menggunakan debugger, debugger akan menghentikan program saat pengecualian dilemparkan. Tanpa debugger terlampir, pengecualian diabaikan secara diam-diam dalam kedua kasus.

_ juga merupakan pengidentifikasi yang valid. Saat digunakan di luar konteks yang didukung, _ diperlakukan bukan sebagai buangan tetapi sebagai variabel yang valid. Jika pengenal bernama _ sudah ada dalam cakupan, penggunaan _ sebagai pembuangan mandiri dapat mengakibatkan:

  • Modifikasi nilai variabel _ dalam cakupan yang tidak disengaja dengan menetapkannya nilai pembuangan yang dimaksudkan. Misalnya:
    private static void ShowValue(int _)
    {
       byte[] arr = [0, 0, 1, 2];
       _ = BitConverter.ToInt32(arr, 0);
       Console.WriteLine(_);
    }
     // The example displays the following output:
     //       33619968
    
  • Kesalahan pengompilasi untuk melanggar keamanan jenis. Misalnya:
    private static bool RoundTrips(int _)
    {
       string value = _.ToString();
       int newValue = 0;
       _ = Int32.TryParse(value, out newValue);
       return _ == newValue;
    }
    // The example displays the following compiler error:
    //      error CS0029: Cannot implicitly convert type 'bool' to 'int'
    
  • Kesalahan pengkompilasi CS0136, "Lokal atau parameter bernama '_' tidak dapat dideklarasikan dalam cakupan ini karena nama tersebut digunakan dalam cakupan lokal yang mencakup untuk menentukan lokal atau parameter." Misalnya:
     public void DoSomething(int _)
    {
     var _ = GetValue(); // Error: cannot declare local _ when one is already in scope
    }
    // The example displays the following compiler error:
    // error CS0136:
    //       A local or parameter named '_' cannot be declared in this scope
    //       because that name is used in an enclosing local scope
    //       to define a local or parameter
    

Lihat juga