Setelah saya memindahkan proyek saya dari VS2013 ke VS2015, proyek tersebut tidak lagi dibangun. Terjadi galat kompilasi dalam pernyataan LINQ berikut ini:
static void Main(string[] args)
{
decimal a, b;
IEnumerable<dynamic> array = new string[] { "10", "20", "30" };
var result = (from v in array
where decimal.TryParse(v, out a) && decimal.TryParse("15", out b) && a <= b // Error here
orderby decimal.Parse(v)
select v).ToArray();
}
Kompilator mengembalikan kesalahan:
Kesalahan CS0165 Penggunaan variabel lokal 'b' yang tidak ditetapkan
Apa penyebab masalah ini? Apakah mungkin untuk memperbaikinya melalui pengaturan kompilator?
b
setelah menetapkannya melaluiout
parameter.out
argumen. Apakah ituTryParse
mengembalikan nilai nullable (atau setara).where (a = decimal.TryParse(v)).HasValue && (b = decimal.TryParse(v)).HasValue && a <= b
terlihat jauh lebih baikdecimal a, b; var q = decimal.TryParse((dynamic)"10", out a) && decimal.TryParse("15", out b) && a <= b;
. Saya telah membuka bug Roslyn yang mengangkat ini.Jawaban:
Sepertinya bug kompiler bagi saya. Setidaknya, memang begitu. Meskipun ekspresi
decimal.TryParse(v, out a)
dandecimal.TryParse(v, out b)
dievaluasi secara dinamis, saya berharap kompilator masih memahami bahwa pada saat mencapaia <= b
, keduanyaa
danb
sudah pasti ditetapkan. Bahkan dengan keanehan yang dapat Anda temukan dalam pengetikan dinamis, saya berharap untuk hanya mengevaluasia <= b
setelah mengevaluasi keduaTryParse
panggilan tersebut.Namun, ternyata melalui operator dan konversi yang rumit, sangat mungkin untuk memiliki ekspresi
A && B && C
yang mengevaluasiA
danC
tetapi tidakB
- jika Anda cukup licik. Lihat laporan bug Roslyn untuk contoh cerdik Neal Gafter.Membuatnya bekerja dengan
dynamic
lebih sulit - semantik yang terlibat saat operan dinamis lebih sulit untuk dijelaskan, karena untuk melakukan resolusi kelebihan beban, Anda perlu mengevaluasi operan untuk mengetahui jenis apa yang terlibat, yang dapat menjadi kontra-intuitif. Namun, sekali lagi Neal memberikan contoh yang menunjukkan bahwa kesalahan kompilator diperlukan ... ini bukan bug, ini perbaikan bug . Pujian dalam jumlah besar untuk Neal karena telah membuktikannya.Tidak, tetapi ada alternatif yang menghindari kesalahan tersebut.
Pertama, Anda dapat menghentikannya agar tidak dinamis - jika Anda tahu bahwa Anda hanya akan pernah menggunakan string, maka Anda dapat menggunakan
IEnumerable<string>
atau memberikan variabel rentangv
jenisstring
(yaitufrom string v in array
). Itu akan menjadi pilihan yang saya sukai.Jika Anda benar - benar perlu menjaganya tetap dinamis, berikan
b
nilai sebagai permulaan:Ini tidak akan merugikan - kami tahu bahwa sebenarnya evaluasi dinamis Anda tidak akan melakukan sesuatu yang gila, jadi Anda tetap akan menetapkan nilai
b
sebelum Anda menggunakannya, membuat nilai awal tidak relevan.Selain itu, tampaknya menambahkan tanda kurung juga berfungsi:
Itu mengubah titik di mana berbagai bagian resolusi berlebih dipicu, dan kebetulan membuat kompiler senang.
Ada satu masalah yang masih tersisa - aturan spesifikasi tentang penetapan pasti dengan
&&
operator perlu diklarifikasi untuk menyatakan bahwa mereka hanya berlaku ketika&&
operator sedang digunakan dalam implementasi "reguler" dengan duabool
operan. Saya akan mencoba untuk memastikan ini diperbaiki untuk standar ECMA berikutnya.sumber
IEnumerable<string>
atau menambahkan tanda kurung berhasil untuk saya. Sekarang kompilator membangun tanpa kesalahan.decimal a, b = 0m;
mungkin menghapus kesalahan, tapia <= b
akan selalu digunakan0m
, karena nilai keluar belum dihitung.decimal? TryParseDecimal(string txt)
mungkin solusi jugab
mungkin tidak diberikan"; Saya tahu itu alasan yang tidak valid tetapi menjelaskan mengapa tanda kurung memperbaikinya ...Ini tampaknya bug, atau setidaknya regresi, dalam kompiler Roslyn. Bug berikut telah diajukan untuk melacaknya:
Sementara itu, jawaban bagus Jon memiliki beberapa solusi.
sumber
Karena saya disekolahkan begitu keras dalam laporan bug, saya akan mencoba menjelaskannya sendiri.
Bayangkan
T
adalah beberapa tipe yang ditentukan pengguna dengan pemeran implisitbool
yang bergantian antarafalse
dantrue
, dimulai denganfalse
. Sejauh yang diketahui kompilator,dynamic
argumen pertama ke argumen pertama&&
mungkin mengevaluasi tipe itu, jadi itu harus pesimis.Jika, kemudian, membiarkan kode dikompilasi, ini bisa terjadi:
&&
, ia melakukan hal berikut:T
- secara implisit melemparkannya kebool
.false
, jadi kita tidak perlu mengevaluasi argumen kedua.&&
evaluasi sebagai argumen pertama. (Tidak, tidakfalse
, untuk beberapa alasan.)&&
, ia melakukan hal berikut:T
- secara implisit melemparkannya kebool
.true
, jadi evaluasi argumen kedua.b
tidak ditugaskan.Dalam istilah spesifikasi, singkatnya, ada aturan khusus "penugasan pasti" yang memungkinkan kita mengatakan tidak hanya apakah suatu variabel "ditetapkan secara pasti" atau "tidak ditetapkan secara pasti", tetapi juga jika ia "ditetapkan secara pasti setelah
false
pernyataan" atau "pasti ditugaskan setelahtrue
pernyataan ".Ini ada sehingga ketika berhadapan dengan
&&
dan||
(dan!
dan??
dan?:
) kompilator dapat memeriksa apakah variabel dapat ditugaskan di cabang tertentu dari ekspresi boolean kompleks.Namun, ini hanya berfungsi jika tipe ekspresi tetap boolean . Jika bagian dari ekspresi adalah
dynamic
(atau tipe statis non-boolean), kami tidak dapat lagi mengatakan dengan andal bahwa ekspresi tersebut adalahtrue
ataufalse
- saat berikutnya kami mentransmisikannyabool
untuk memutuskan cabang mana yang akan diambil, mungkin telah berubah pikiran.Pembaruan: ini sekarang telah diselesaikan dan didokumentasikan :
sumber
out
metode yang memilikiref
. Ini akan dengan senang hati melakukannya, dan itu akan membuat variabel ditugaskan, tanpa mengubah nilainya.false
/true
sebagai lawan dari operator cast implisit? Secara lokal, itu akan memanggilimplicit operator bool
argumen pertama, kemudian memanggil operan kedua, memanggiloperator false
operan pertama, diikuti olehimplicit operator bool
pada operan pertama lagi . Ini tidak masuk akal bagi saya, operan pertama pada dasarnya harus mendidih menjadi boolean sekali, bukan?dynamic
, dirantai&&
? Saya telah melihatnya pada dasarnya pergi (1) mengevaluasi argumen pertama (2) menggunakan pemeran implisit untuk melihat apakah saya dapat korsleting (3) Saya tidak bisa, jadi hindari argumen kedua (4) sekarang saya tahu kedua jenis, saya dapat melihat yang terbaik&&
adalah&
operator panggilan yang ditentukan pengguna (5)false
pada argumen pertama untuk melihat apakah saya dapat hubungan pendek (6) saya bisa (karenafalse
danimplicit bool
tidak setuju), jadi hasilnya adalah argumen pertama ... dan kemudian berikutnya&&
, (7) gunakan pemeran implisit untuk melihat apakah saya dapat mengalami korsleting (lagi).Ini bukan bug. Lihat https://github.com/dotnet/roslyn/issues/4509#issuecomment-130872713 untuk contoh bagaimana ekspresi dinamis dari formulir ini dapat membiarkan variabel seperti itu tidak ditetapkan.
sumber