Bagaimana saya bisa default parameter ke Guid.Empty di C #?

178

Saya ingin mengatakan:

public void Problem(Guid optional = Guid.Empty)
{
}

Tetapi kompiler mengeluh bahwa Guid.Empty bukan konstanta waktu kompilasi.

Karena saya tidak ingin mengubah API saya tidak bisa menggunakan:

 Nullable<Guid>
Ian Ringrose
sumber
Apa yang salah dengan beralih ke Nullable<Guid> optional = null(atau lebih tepatnya, Guid? optional = null)? Setiap Panduan yang saat ini diteruskan akan dipaksakan tanpa perubahan kode apa pun yang diperlukan sama sekali.
NH.

Jawaban:

235

Larutan

Anda dapat menggunakan new Guid()sebagai gantinya

public void Problem(Guid optional = new Guid())
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

Anda juga bisa menggunakan default(Guid)

default(Guid)juga akan bekerja persis seperti new Guid().

Karena Guid adalah tipe nilai bukan tipe referensi, jadi, default(Guid)tidak sama dengan nullmisalnya, sebaliknya, itu sama dengan memanggil konstruktor default.

Yang berarti ini:

public void Problem(Guid optional = default(Guid))
{
  // when called without parameters this will be true
  var guidIsEmpty = optional == Guid.Empty;
}

Persis sama dengan contoh aslinya.

Penjelasan

Kenapa tidak Guid.Emptyberhasil?

Alasan Anda mendapatkan kesalahan adalah karena Emptydidefinisikan sebagai:

public static readonly Guid Empty;

Jadi, ini adalah variabel, bukan konstanta (didefinisikan sebagai static readonlybukan sebagai const). Compiler hanya dapat memiliki nilai-nilai yang diketahui-compiler sebagai parameter metode nilai-nilai default (tidak-hanya-runtime-dikenal).

Akar penyebab adalah bahwa Anda tidak dapat memiliki constapapunstruct satunya, tidak seperti enummisalnya. Jika Anda mencobanya, itu tidak akan dikompilasi.

Alasan sekali lagi adalah itu structbukan tipe primitif.
Untuk daftar semua tipe primitif di .NET lihat http://msdn.microsoft.com/en-gb/library/system.typecode.aspx
(catatan yang enumbiasanya mewarisiint , yang merupakan primitif)

Tapi new Guid() tidak konstan juga!

Saya tidak mengatakan itu perlu konstan. Perlu sesuatu yang dapat diputuskan dalam waktu kompilasi.Emptyis a field, jadi, nilainya tidak diketahui dalam waktu kompilasi (hanya pada awal waktu menjalankan).

Nilai parameter default harus diketahui pada waktu kompilasi, yang dapat berupa constnilai, atau sesuatu yang didefinisikan menggunakan fitur C # yang membuat nilai diketahui pada waktu kompilasi, seperti default(Guid)atau new Guid()(yang ditentukan pada waktu kompilasi untuk structs karena Anda tidak dapat memodifikasistruct konstruktor dalam kode).

Meskipun Anda dapat memberikan defaultatau newdengan mudah, Anda tidak dapat memberikan const(karena itu bukan tipe primitif atau enumseperti yang dijelaskan di atas). Jadi, sekali lagi, tidak mengatakan bahwa parameter opsional itu sendiri membutuhkan nilai konstanta, tetapi diketahui kompiler.

Meligy
sumber
5
Yah, itu tidak membutuhkan ekspresi konstan yang normal - new Guid()bukan ekspresi konstan, misalnya. C # spec mendefinisikan dengan sangat jelas apa yang diperbolehkan, termasuk tetapi tidak terbatas pada konstanta. (Hanya untuk menjadi jelas, ini secara efektif adalah kompilasi konstanta waktu, hanya saja bukan "ekspresi konstan" dalam istilah spesifikasi C #).
Jon Skeet
3
Baca bagian Penjelasan dalam balasan saya. Ditambahkan untuk menjawab bagian ini.
Meligy
Penjelasan yang bagus. Terima kasih!
Daryl
Anda hanya dapat menggunakan defaultsaat ini :)
Joshit
151

Guid.Emptysetara dengan new Guid(), yang setara dengan default(Guid). Jadi Anda bisa menggunakan:

public void Problem(Guid optional = default(Guid))

atau

public void Problem(Guid optional = new Guid())

Perhatikan bahwa new Foo()nilai hanya berlaku ketika:

  • Anda benar-benar memanggil konstruktor tanpa parameter
  • Foo adalah tipe nilai

Dengan kata lain, ketika kompiler tahu itu benar-benar hanya nilai default untuk tipe :)

(Menariknya, saya 99,9% yakin itu tidak akan memanggil new Foo()konstruktor kustom apa pun yang mungkin Anda buat. Anda tidak dapat membuat konstruktor semacam itu dalam tipe nilai dalam C #, tetapi Anda dapat melakukannya di IL.)

Anda dapat menggunakan default(Foo)opsi untuk jenis apa pun .

Jon Skeet
sumber
Sekarang mengapa pesan kesalahan kompiler tidak memberi tahu saya hal ini, kompilator dapat memeriksa kasus Guid.Empty dan memberikan pesan yang lebih bermanfaat.
Ian Ringrose
4
@Ian Ringrose: Saya kira kompiler seharusnya tidak memiliki tipe pesan spesifik secara umum, jujur ​​saja.
Jon Skeet
2
Mengatur parameter ke default ke objek baru menciptakan objek baru setiap kali metode dipanggil dalam PHP; tetapi hanya membuat satu objek untuk seluruh program dengan Python. Sungguh, saya menganggap ini sebagai salah satu dari sedikit kekurangan desain Python . Saya agak senang C # (dan VB.Net) menghindari masalah ini dengan hanya melarang objek baru dalam parameter default ... meskipun ada kalanya kemampuan ini sangat bagus di PHP.
BlueRaja - Danny Pflughoeft
1
apa yang bisa menjadi alasan bahwa hal yang sama ini tidak akan berfungsi dalam tindakan kontroler ASP.NET MVC? Semua parameter opsional lain berfungsi (int, string) tetapi tidak berfungsi untuk GUID, kata "Server Error di '/' Aplikasi. Kamus parameter berisi entri nol untuk parameter 'categoryId' dari tipe yang tidak dapat dibatalkan 'System.Guid '.. "Kode mengkompilasi dengan baik dengan spesifikasi (default (Guid) dan dengan Guid baru ()) tetapi mengeluarkan kesalahan ini.
mare
@ Mimpi: Saya tidak tahu, saya takut. Pilihan lain adalah menggunakan Nullable<Guid>, berpotensi.
Jon Skeet
18

Tidak bisakah kamu menggunakan:

default ( Guid ) ?

Nick
sumber
1
No. Operator '??' cannot be applied to operands of type 'System.Guid' and 'System.Guid'
rekursif
4
Maaf, maksud saya bukan ?? sebagai operator tetapi sebagai tanda tanya yang ditekankan - saya akan mengedit!
Nick
9

Jawaban yang diterima tidak berfungsi di ASP.NET MVC, dan menyebabkan kesalahan saat ini:

[ArgumentException: The parameters dictionary contains a null entry for parameter 'optional' of non-nullable type 'System.Guid' for method 'System.Web.Mvc.ActionResult Problem(System.Guid)' ....

Sebagai gantinya, Anda dapat melakukan hal berikut:

public void Problem(Guid? optional)
{
    if (optional == null)
    {
        optional = new Guid();
    }
}
Majix
sumber
Saya melihat alasan untuk memilih turun: Pertanyaan menentukan bahwa API tidak dapat diubah untuk menggunakan Nullable <Guid> - cukup adil
Majix
Kecuali jika Anda ingin secara eksplisit menetapkan paramter "opsional" dengan nilai Guid kosong, saya pikir ini adalah cara paling alami untuk mendefinisikan parameter opsional tipe Guid.
Gonzalo Méndez
4

Kompilernya cukup benar; Guid.Emptybukan konstanta waktu kompilasi. Anda dapat mencoba membuat metode kelebihan seperti ini:

public void Problem()
{
    Problem(Guid.Empty);
}
sebuah CVn
sumber
Saya bilang saya tidak ingin mengubah API, metode yang saya coba jinakkan sudah melebihi 10 parm!
Ian Ringrose
@Ian Ringrose, meskipun saya setuju dengan Guid x = default(Guid)sebagai solusinya, ingat bahwa menambahkan fungsi lain yang berlebihan tidak mempersulit API lebih dari menambahkan argumen opsional. Sebenarnya itulah yang dilakukan argumen opsional.
Tenfour
@tenfour, harus ada banyak kelebihan fungsi baru untuk melakukan hal yang sama seperti 10 parm opsional!
Ian Ringrose
Jika Anda memiliki fungsi publik yang membutuhkan lebih dari sepuluh parameter, mungkin membuat argumen tunggal opsional tidak benar-benar memperbaiki ... Juga, melihat kembali pertanyaan aslinya, Anda memang mengatakan bahwa Anda tidak ingin "mengubah API "(dan seperti yang ditunjukkan oleh tenfour, perbedaan antara overload eksplisit dan argumen opsional sangat minim dalam praktiknya) tetapi tidak disebutkan dalam pertanyaan tentang daftar parameter yang berupa monster seperti itu.
CVn
Sebenarnya, ini adalah jawaban sempurna untuk masalah tersebut.
PKD