Saya memiliki skenario di mana saya ingin menggunakan sintaks grup metode daripada metode anonim (atau sintaks lambda) untuk memanggil suatu fungsi.
Fungsi ini memiliki dua beban berlebih, satu kelebihan beban, yang Action
lainnya menerima Func<string>
.
Saya dengan senang hati dapat memanggil dua kelebihan beban menggunakan metode anonim (atau sintaks lambda), tetapi mendapatkan kesalahan penyusun dari pemanggilan ambigu jika saya menggunakan sintaks grup metode. Saya dapat mengatasinya dengan mentransmisikan secara eksplisit ke Action
atau Func<string>
, tetapi menurut saya ini tidak perlu.
Adakah yang bisa menjelaskan mengapa pemeran eksplisit harus diperlukan.
Contoh kode di bawah ini.
class Program
{
static void Main(string[] args)
{
ClassWithSimpleMethods classWithSimpleMethods = new ClassWithSimpleMethods();
ClassWithDelegateMethods classWithDelegateMethods = new ClassWithDelegateMethods();
// These both compile (lambda syntax)
classWithDelegateMethods.Method(() => classWithSimpleMethods.GetString());
classWithDelegateMethods.Method(() => classWithSimpleMethods.DoNothing());
// These also compile (method group with explicit cast)
classWithDelegateMethods.Method((Func<string>)classWithSimpleMethods.GetString);
classWithDelegateMethods.Method((Action)classWithSimpleMethods.DoNothing);
// These both error with "Ambiguous invocation" (method group)
classWithDelegateMethods.Method(classWithSimpleMethods.GetString);
classWithDelegateMethods.Method(classWithSimpleMethods.DoNothing);
}
}
class ClassWithDelegateMethods
{
public void Method(Func<string> func) { /* do something */ }
public void Method(Action action) { /* do something */ }
}
class ClassWithSimpleMethods
{
public string GetString() { return ""; }
public void DoNothing() { }
}
Pembaruan C # 7.3
Sesuai komentar 0xcde di bawah ini pada 20 Maret 2019 (sembilan tahun setelah saya memposting pertanyaan ini!), Kode ini dikompilasi pada C # 7.3 berkat peningkatan kandidat yang kelebihan beban .
<LangVersion>7.3</LangVersion>
) atau lebih baru berkat kandidat kelebihan beban yang ditingkatkan .Jawaban:
Pertama, izinkan saya mengatakan bahwa jawaban Jon benar. Ini adalah salah satu bagian paling hairiest dari spesifikasi, sangat bagus bagi Jon untuk menyelami kepalanya terlebih dahulu.
Kedua, izinkan saya mengatakan bahwa baris ini:
(penekanan ditambahkan) sangat menyesatkan dan sangat disayangkan. Saya akan berbicara dengan Mads tentang menghapus kata "kompatibel" di sini.
Alasan mengapa hal ini menyesatkan dan tidak menguntungkan adalah karena sepertinya ini memanggil bagian 15.2, "Kompatibilitas delegasi". Bagian 15.2 menjelaskan hubungan kompatibilitas antara metode dan tipe delegasi , tetapi ini adalah pertanyaan tentang konvertibilitas grup metode dan tipe delegasi , yang berbeda.
Sekarang setelah kita menyelesaikannya, kita dapat berjalan melalui bagian 6.6 dari spesifikasi dan melihat apa yang kita dapatkan.
Untuk melakukan resolusi kelebihan beban, pertama-tama kita perlu menentukan kelebihan beban mana yang merupakan kandidat yang berlaku . Kandidat dapat diterapkan jika semua argumen secara implisit dapat diubah menjadi tipe parameter formal. Pertimbangkan versi program Anda yang disederhanakan ini:
Jadi mari kita bahas baris demi baris.
Saya sudah membahas bagaimana kata "kompatibel" tidak menguntungkan di sini. Bergerak. Kami bertanya-tanya ketika melakukan resolusi kelebihan beban pada Y (X), apakah metode grup X dikonversi ke D1? Apakah itu dikonversi ke D2?
Sejauh ini baik. X mungkin berisi metode yang dapat diterapkan dengan daftar argumen D1 atau D2.
Baris ini benar-benar tidak mengatakan sesuatu yang menarik.
Garis ini sangat menarik. Ini berarti bahwa ada konversi implisit yang ada, tetapi dapat berubah menjadi kesalahan! Ini adalah aturan C # yang aneh. Untuk menyimpang sejenak, berikut contohnya:
Operasi increment ilegal di pohon ekspresi. Namun, lambda masih dapat diubah menjadi tipe pohon ekspresi, meskipun jika konversi pernah digunakan, itu adalah kesalahan! Prinsipnya di sini adalah bahwa kita mungkin ingin mengubah aturan tentang apa yang bisa masuk dalam pohon ekspresi nanti; mengubah aturan tersebut seharusnya tidak mengubah aturan sistem jenis . Kami ingin memaksa Anda untuk membuat program Anda tidak ambigu sekarang , sehingga saat kami mengubah aturan pohon ekspresi di masa mendatang untuk membuatnya lebih baik, kami tidak memperkenalkan perubahan yang melanggar dalam resolusi kelebihan beban .
Bagaimanapun, ini adalah contoh lain dari aturan aneh semacam ini. Sebuah konversi bisa saja ada untuk tujuan resolusi yang berlebihan, tapi bisa menjadi kesalahan untuk benar-benar digunakan. Padahal sebenarnya, bukan itu situasi kita saat ini.
Bergerak:
BAIK. Jadi kami melakukan resolusi overload pada X sehubungan dengan D1. Daftar parameter formal D1 kosong, jadi kami melakukan resolusi berlebih pada X () dan kegembiraan, kami menemukan metode "string X ()" yang berfungsi. Demikian pula, daftar parameter formal D2 kosong. Sekali lagi, kami menemukan bahwa "string X ()" adalah metode yang berfungsi di sini juga.
Prinsipnya di sini adalah bahwa menentukan konvertibilitas grup metode memerlukan pemilihan metode dari grup metode menggunakan resolusi kelebihan beban , dan resolusi kelebihan beban tidak mempertimbangkan jenis kembalian .
Hanya ada satu metode dalam metode grup X, jadi itu pasti yang terbaik. Kami telah berhasil membuktikan bahwa ada konversi dari X ke D1 dan dari X ke D2.
Sekarang, apakah baris ini relevan?
Sebenarnya, tidak, tidak dalam program ini. Kami tidak pernah sejauh mengaktifkan baris ini. Karena, ingat, yang kami lakukan di sini adalah mencoba melakukan resolusi kelebihan beban pada Y (X). Kami memiliki dua kandidat Y (D1) dan Y (D2). Keduanya dapat diterapkan. Mana yang lebih baik ? Tidak ada dalam spesifikasi yang kami gambarkan tentang kehebatan antara dua kemungkinan konversi ini .
Sekarang, orang pasti dapat berargumen bahwa konversi yang valid lebih baik daripada konversi yang menghasilkan kesalahan. Itu kemudian akan secara efektif mengatakan, dalam kasus ini, bahwa resolusi kelebihan beban TIDAK mempertimbangkan jenis kembalian, yang merupakan sesuatu yang ingin kita hindari. Pertanyaannya kemudian adalah prinsip mana yang lebih baik: (1) pertahankan invarian bahwa resolusi kelebihan beban tidak mempertimbangkan jenis pengembalian, atau (2) mencoba memilih konversi yang kita tahu akan berhasil yang kita tahu tidak akan?
Ini adalah panggilan penghakiman. Dengan lambda , kami melakukannya mempertimbangkan tipe kembali di semacam ini konversi, di bagian 7.4.3.3:
Sayangnya, konversi grup metode dan konversi lambda tidak konsisten dalam hal ini. Namun, saya bisa menerimanya.
Bagaimanapun, kami tidak memiliki aturan "betterness" untuk menentukan konversi mana yang lebih baik, X ke D1 atau X ke D2. Oleh karena itu kami memberikan kesalahan ambiguitas pada resolusi Y (X).
sumber
EDIT: Saya pikir saya sudah mendapatkannya.
Seperti kata zinglon, itu karena ada konversi implisit dari
GetString
menjadiAction
meskipun aplikasi waktu kompilasi akan gagal. Berikut pengantar ke bagian 6.6, dengan beberapa penekanan (milik saya):Sekarang, saya menjadi bingung dengan kalimat pertama - yang berbicara tentang konversi ke tipe delegasi yang kompatibel.
Action
bukan delegasi yang kompatibel untuk metode apa pun dalamGetString
grup metode, tetapiGetString()
metode ini dapat diterapkan dalam bentuk normalnya ke daftar argumen yang dibuat dengan menggunakan tipe parameter dan pengubah D. Perhatikan bahwa ini tidak berbicara tentang tipe kembalian dari D. Itulah mengapa ini menjadi membingungkan ... karena hanya akan memeriksa kompatibilitas delegasiGetString()
ketika menerapkan konversi, bukan memeriksa keberadaannya.Saya pikir itu instruktif untuk meninggalkan persamaan secara singkat, dan melihat bagaimana perbedaan antara keberadaan konversi dan penerapannya dapat terwujud. Berikut contoh singkat tapi lengkap:
Tak satu pun dari ekspresi pemanggilan metode dalam
Main
kompilasi, tetapi pesan kesalahannya berbeda. Ini dia untukIntMethod(GetString)
:Dengan kata lain, bagian 7.4.3.1 dari spesifikasi tidak dapat menemukan anggota fungsi yang berlaku.
Sekarang, inilah kesalahan untuk
ActionMethod(GetString)
:Kali ini berhasil menemukan metode yang ingin dipanggil - tetapi gagal kemudian melakukan konversi yang diperlukan. Sayangnya saya tidak dapat menemukan sedikit pun dari spesifikasi tempat pemeriksaan terakhir itu dilakukan - sepertinya itu mungkin di 7.5.5.1, tetapi saya tidak dapat melihat persis di mana.
Jawaban lama dihapus, kecuali untuk bagian ini - karena saya berharap Eric bisa menjelaskan "mengapa" dari pertanyaan ini ...
Masih mencari ... sementara itu, jika kita mengatakan "Eric Lippert" tiga kali, apakah menurut Anda kita akan mendapat kunjungan (dan dengan demikian jawaban)?
sumber
classWithSimpleMethods.GetString
danclassWithSimpleMethods.DoNothing
bukan delegasi?ClassWithSimpleMethods.GetString
keAction
adalah valid? Untuk metodeM
agar kompatibel dengan tipe delegasiD
(§15.2) "identitas atau konversi referensi implisit ada dari tipe kembalianM
ke tipe kembalianD
."Menggunakan
Func<string>
danAction<string>
(jelas sangat berbeda denganAction
danFunc<string>
) di fileClassWithDelegateMethods
menghilangkan ambiguitas.Ambiguitas juga terjadi antara
Action
danFunc<int>
.Saya juga mendapatkan kesalahan ambiguitas dengan ini:
Eksperimen lebih lanjut menunjukkan bahwa saat meneruskan grup metode sendiri, jenis kembalian sepenuhnya diabaikan saat menentukan kelebihan beban yang akan digunakan.
sumber
Overloading dengan
Func
danAction
serupa (karena keduanya adalah delegasi)Jika Anda perhatikan, kompilator tidak tahu mana yang harus dipanggil karena mereka hanya berbeda menurut tipe kembalian.
sumber
Func<string>
menjadiAction
... dan Anda tidak dapat mengonversi grup metode yang hanya terdiri dari metode yang mengembalikan string menjadiAction
keduanya.string
keAction
. Saya tidak mengerti mengapa ada ambiguitas.