Kapan saya harus menggunakan operator konversi tipe implisit C #?

14

Di C #, kami dapat membebani operator konversi implisit seperti ini (contoh dari MSDN ):

struct Digit
{
    /* ... */
    public static implicit operator byte(Digit d)  // implicit digit to byte conversion operator
    {
        /* ... */
    }
}

Dengan demikian, kita dapat memiliki tipe, tipe nilai khusus , secara ajaib mengubah dirinya menjadi tipe lain (yang tidak terkait), meninggalkan penonton dalam kebingungan (sampai mereka mengintip ke belakang panggung dan melihat operator konversi implisit, yaitu).

Saya tidak suka meninggalkan orang yang membaca kode saya dengan bingung. Saya rasa tidak banyak orang yang tahu.

Pertanyaannya adalah, apa saja kasus penggunaan dari operator konversi tipe implisit yang tidak akan membuat kode saya jauh lebih sulit untuk dipahami?

Mints97
sumber
1
Wow. Saya sebenarnya tidak tahu ini ada. Bukan berarti itu hal yang baik untuk digunakan; Saya tahu orang-orang merasa sangat terganggu dengan fungsi-bersembunyi semacam ini di C ++.
Katana314
@ Katana314: Bukan itu yang membuat orang kesal, tetapi tentang seseorang yang menambahkan kelebihan (baik operator, fungsi konversi, konstruktor, fungsi bebas atau fungsi anggota) dengan perilaku mengejutkan, lebih tepatnya mengejutkan.
Deduplicator
Saya sarankan Anda membaca "overloading operator" di C ++, khususnya operator "casting". Saya menduga banyak argumen yang sama untuk / melawan adalah sama, kecuali perdebatan telah berlangsung tiga kali selama C # telah ada dengan lebih banyak untuk dibaca.

Jawaban:

18

Saya hanya akan merekomendasikan konversi implisit antara jenis yang secara kasar mewakili nilai yang sama dengan cara yang berbeda. Contohnya:

  • Jenis warna yang berbeda seperti RGB, HSL, HSVdanCMYK .
  • Unit berbeda untuk kuantitas fisik yang sama ( Metervs.Inch ).
  • Sistem koordinat yang berbeda (polar vs Cartesian).

Namun, ada beberapa pedoman kuat yang menunjukkan kapan tidak tepat untuk menentukan konversi implisit:

  • Jika konversi menyebabkan hilangnya presisi atau kisaran yang signifikan, maka konversi tidak boleh implisit (mis: dari float64 ke float32 atau dari long ke int).
  • Jika konversi dapat melempar (InvalidCast pengecualian ), maka itu tidak boleh implisit.
  • Jika konversi menyebabkan alokasi tumpukan setiap kali dilakukan, maka itu tidak boleh implisit.
  • Jika konversi bukan O(1)operasi, maka itu tidak boleh implisit.
  • Jika jenis sumber atau jenis target bisa berubah, maka konversi tidak boleh implisit.
  • Jika konversi tergantung pada semacam konteks (basis data, pengaturan kultur, konfigurasi, sistem file, dll.) Maka itu tidak boleh implisit (saya juga akan mencegah operator konversi eksplisit dalam kasus ini).

Sekarang katakan operator konversi f: T1 -> T2Anda tidak melanggar salah satu aturan di atas, maka perilaku berikut sangat menunjukkan bahwa konversi dapat tersirat:

  • Jika a == bdemikian f(a) == f(b).
  • Jika a != bdemikian f(a) != f(b).
  • Jika a.ToString() == b.ToString()demikian f(a).ToString() == f(b).ToString().
  • Dll untuk operasi lain yang didefinisikan pada keduanya T1dan T2.
Elian Ebbing
sumber
Semua contoh Anda mungkin merugi. Apakah mereka cukup tepat, ...
Deduplicator
Ya saya menyadari bahwa :-). Saya tidak bisa memikirkan istilah yang lebih baik untuk "lossy". Yang saya maksud dengan "lossy" adalah konversi di mana kisaran atau ketepatannya berkurang secara substansial. Misalnya dari float64 ke float32 atau dari long ke int.
Elian Ebbing
Saya pikir a! = B => f (a)! = F (b), mungkin seharusnya tidak berlaku. Ada banyak fungsi yang mungkin mengembalikan nilai yang sama untuk input yang berbeda, floor () dan ceil () misalnya di sisi matematika
cdkMoose
@cdkMoose Anda tentu saja benar, dan itulah mengapa saya melihat properti ini lebih sebagai "poin bonus", bukan aturan. Properti kedua berarti bahwa fungsi konversi bersifat injeksi. Ini sering terjadi ketika Anda mengonversi ke jenis yang memiliki rentang yang sangat besar, misalnya dari int32 ke int64.
Elian Ebbing
@cdkMoose Di sisi lain, properti pertama hanya menyatakan bahwa dua nilai dalam kelas ekivalensi yang sama T1(tersirat oleh ==relasi pada T1) selalu memetakan ke dua nilai dalam kelas ekivalensi yang sama yaitu T2. Sekarang saya memikirkannya, saya kira properti pertama sebenarnya harus diperlukan untuk konversi implisit.
Elian Ebbing
6

Pertanyaannya adalah, apa saja kasus penggunaan dari operator konversi tipe implisit yang tidak akan membuat kode saya jauh lebih sulit untuk dipahami?

Ketika jenisnya tidak terkait (untuk programmer). Ada skenario (jarang) di mana Anda memiliki dua jenis yang tidak terkait (sejauh kode terkait), yang sebenarnya terkait (sejauh domain atau programmer yang masuk akal) yang bersangkutan.

Misalnya, beberapa kode melakukan pencocokan string. Skenario umum adalah mencocokkan string literal. Alih-alih menelepon IsMatch(input, new Literal("some string")), konversi implisit memungkinkan Anda menyingkirkan upacara itu - suara dalam kode - dan fokus pada string literal.

Sebagian besar programmer akan melihat IsMatch(input, "some string")dan dengan cepat memahami apa yang sedang terjadi. Itu membuat kode Anda lebih jelas di situs panggilan. Singkatnya, itu membuat sedikit lebih mudah untuk memahami apa yang sedang terjadi, dengan sedikit biaya bagaimana itu terjadi.

Sekarang, Anda mungkin berpendapat bahwa fungsi sederhana kelebihan beban untuk melakukan hal yang sama akan lebih baik. Dan itu. Tetapi jika hal semacam ini ada di mana-mana, maka memiliki satu konversi lebih bersih (lebih sedikit kode, meningkatkan konsistensi) daripada melakukan tumpukan fungsi yang berlebihan.

Dan Anda mungkin berpendapat bahwa lebih baik meminta programmer untuk secara eksplisit membuat tipe perantara sehingga mereka melihat "apa yang sebenarnya terjadi". Itu kurang mudah. Secara pribadi, saya berpikir bahwa contoh pencocokan string literal sangat jelas tentang "apa yang sebenarnya terjadi" - programmer tidak perlu tahu mekanisme tentang bagaimana semuanya terjadi. Apakah Anda tahu bagaimana semua kode Anda dijalankan oleh berbagai prosesor yang dijalankan oleh kode Anda? Selalu ada garis abstraksi di mana programmer berhenti peduli tentang bagaimana sesuatu bekerja. Jika Anda berpikir bahwa langkah-langkah konversi implisit penting, maka jangan gunakan konversi implisit. Jika Anda pikir mereka hanya upacara untuk menjaga komputer bahagia, dan programmer akan lebih baik tidak melihat suara itu di mana-mana,

Telastyn
sumber
Poin terakhir Anda dapat dan harus diambil lebih jauh: Ada juga garis di luar yang programmer lebih baik tidak peduli bagaimana sesuatu dilakukan, karena itu tidak kontraktual.
Deduplicator