Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
Nota
Artikel ini adalah spesifikasi fitur. Spesifikasi berfungsi sebagai dokumen desain untuk fitur tersebut. Ini termasuk perubahan spesifikasi yang diusulkan, bersama dengan informasi yang diperlukan selama desain dan pengembangan fitur. Artikel ini diterbitkan sampai perubahan spesifikasi yang diusulkan diselesaikan dan dimasukkan dalam spesifikasi ECMA saat ini.
Mungkin ada beberapa perbedaan antara spesifikasi fitur dan implementasi yang selesai. Perbedaan tersebut ditangkap dalam catatan rapat desain bahasa terkait (LDM) .
Anda dapat mempelajari lebih lanjut tentang proses untuk mengadopsi speklet fitur ke dalam standar bahasa C# dalam artikel tentang spesifikasi .
Masalah utama: https://github.com/dotnet/csharplang/issues/4934
Ringkasan
Perubahan yang diusulkan:
- Mengizinkan lambda dengan atribut
- Izinkan lambda dengan jenis pengembalian eksplisit
- Menyimpulkan jenis delegasi alami untuk lambda dan grup metode
Motivasi
Dukungan untuk atribut pada lambda akan memberikan paritas dengan metode dan fungsi lokal.
Dukungan untuk jenis pengembalian eksplisit akan menyediakan simetri dengan parameter lambda di mana jenis eksplisit dapat ditentukan. Memungkinkan jenis pengembalian eksplisit juga akan memberikan kontrol atas performa pengkompilasi di lambda berlapis di mana resolusi kelebihan beban harus mengikat isi lambda saat ini untuk menentukan tanda tangan.
Tipe alami untuk ekspresi lambda dan grup metode akan memungkinkan lebih banyak skenario di mana lambda dan grup metode dapat digunakan tanpa tipe delegasi eksplisit, termasuk sebagai penginisialisasi dalam deklarasi var.
Memerlukan jenis delegasi eksplisit untuk lambda dan grup metode menimbulkan ketidaknyamanan bagi pelanggan, dan telah menjadi hambatan bagi kemajuan di ASP.NET dengan pekerjaan baru-baru ini pada MapAction.
MapAction ASP.NET tanpa perubahan yang diusulkan (MapAction() menerima argumen System.Delegate):
[HttpGet("/")] Todo GetTodo() => new(Id: 0, Name: "Name");
app.MapAction((Func<Todo>)GetTodo);
[HttpPost("/")] Todo PostTodo([FromBody] Todo todo) => todo;
app.MapAction((Func<Todo, Todo>)PostTodo);
ASP.NET MapAction dengan tipe alami untuk grup metode:
[HttpGet("/")] Todo GetTodo() => new(Id: 0, Name: "Name");
app.MapAction(GetTodo);
[HttpPost("/")] Todo PostTodo([FromBody] Todo todo) => todo;
app.MapAction(PostTodo);
ASP.NET MapAction dengan atribut dan tipe alami untuk ekspresi lambda:
app.MapAction([HttpGet("/")] () => new Todo(Id: 0, Name: "Name"));
app.MapAction([HttpPost("/")] ([FromBody] Todo todo) => todo);
Atribut
Atribut dapat ditambahkan ke ekspresi lambda dan parameter lambda. Untuk menghindari ambiguitas antara atribut metode dan atribut parameter, ekspresi lambda dengan atribut harus menggunakan daftar parameter yang dikurung. Jenis parameter tidak diperlukan.
f = [A] () => { }; // [A] lambda
f = [return:A] x => x; // syntax error at '=>'
f = [return:A] (x) => x; // [A] lambda
f = [A] static x => x; // syntax error at '=>'
f = ([A] x) => x; // [A] x
f = ([A] ref int x) => x; // [A] x
Beberapa atribut dapat ditentukan, baik dipisahkan koma dalam daftar atribut yang sama atau sebagai daftar atribut terpisah.
var f = [A1, A2][A3] () => { }; // ok
var g = ([A1][A2, A3] int x) => x; // ok
Atribut tidak didukung untuk metode anonim dideklarasikan dengan sintaks delegate { }.
f = [A] delegate { return 1; }; // syntax error at 'delegate'
f = delegate ([A] int x) { return x; }; // syntax error at '['
Pengurai akan melihat ke depan untuk membedakan penginisialisasi koleksi dengan penetapan elemen dari penginisialisasi koleksi dengan ekspresi lambda.
var y = new C { [A] = x }; // ok: y[A] = x
var z = new C { [A] x => x }; // ok: z[0] = [A] x => x
Pengurai akan memperlakukan ?[ sebagai awal akses elemen bersyarat.
x = b ? [A]; // ok
y = b ? [A] () => { } : z; // syntax error at '('
Atribut pada ekspresi lambda atau parameter lambda akan dipancarkan ke metadata pada metode yang memetakan ke lambda.
Secara umum, pelanggan tidak boleh bergantung pada bagaimana ekspresi lambda dan fungsi lokal memetakan dari sumber ke metadata. Bagaimana cara lambda dan fungsi lokal dihasilkan dapat berubah, dan memang telah berubah di antara versi kompilator.
Perubahan yang diusulkan di sini ditujukan untuk skenario berbasis Delegate.
Harus valid untuk memeriksa MethodInfo yang terkait dengan instans Delegate untuk menentukan tanda tangan ekspresi lambda atau fungsi lokal termasuk atribut eksplisit dan metadata tambahan yang dikeluarkan oleh pengkompilasi seperti parameter default.
Ini memungkinkan tim seperti ASP.NET untuk menyediakan perilaku yang sama untuk lambda dan fungsi lokal seperti yang tersedia pada metode biasa.
Jenis pengembalian eksplisit
Jenis pengembalian eksplisit dapat ditentukan sebelum daftar parameter yang dikurung.
f = T () => default; // ok
f = short x => 1; // syntax error at '=>'
f = ref int (ref int x) => ref x; // ok
f = static void (_) => { }; // ok
f = async async (async async) => async; // ok?
Pengurai akan melihat ke depan untuk membedakan panggilan metode T() dari ekspresi lambda T () => e.
Jenis pengembalian eksplisit tidak didukung untuk metode anonim yang dideklarasikan dengan sintaks delegate { }.
f = delegate int { return 1; }; // syntax error
f = delegate int (int x) { return x; }; // syntax error
Inferensi tipe metode harus membuat inferensi yang tepat dari tipe pengembalian lambda yang eksplisit.
static void F<T>(Func<T, T> f) { ... }
F(int (i) => i); // Func<int, int>
Konversi varians tidak diizinkan dari jenis pengembalian lambda ke jenis pengembalian delegasi (sesuai dengan perilaku serupa untuk tipe parameter).
Func<object> f1 = string () => null; // error
Func<object?> f2 = object () => x; // warning
Pengurai memungkinkan ekspresi lambda dengan ref mengembalikan jenis dalam ekspresi tanpa tanda kurung tambahan.
d = ref int () => x; // d = (ref int () => x)
F(ref int () => x); // F((ref int () => x))
var tidak dapat digunakan sebagai jenis pengembalian eksplisit untuk ekspresi lambda.
class var { }
d = var (var v) => v; // error: contextual keyword 'var' cannot be used as explicit lambda return type
d = @var (var v) => v; // ok
d = ref var (ref var v) => ref v; // error: contextual keyword 'var' cannot be used as explicit lambda return type
d = ref @var (ref var v) => ref v; // ok
Jenis alami (fungsi)
Ekspresi fungsi anonim (§12,19) (ekspresi lambda atau anonim metode) memiliki jenis alami jika jenis parameter eksplisit dan jenis pengembaliannya eksplisit atau dapat disimpulkan (lihat §12.6.3.13).
Grup metode tersebut,, memiliki jenis alami jika semua metode kandidat dalam grup tersebut memiliki tanda tangan yang sama. (Jika grup metode dapat mencakup metode ekstensi, kandidat menyertakan jenis yang berisi dan semua cakupan metode ekstensi.)
Jenis alami ekspresi fungsi anonim atau grup metode adalah function_type. function_type mewakili signature metode: jenis parameter dan jenis referensi, serta jenis pengembalian dan jenis referensi. Ekspresi fungsi anonim atau grup metode dengan tanda tangan yang sama memiliki function_typeyang sama.
Function_types hanya digunakan dalam beberapa konteks tertentu:
- konversi implisit dan eksplisit
- inferensi tipe metode (§12.6.3) dan tipe umum terbaik (§12.6.3.15)
- penginisialisasi
var
function_type hanya ada pada waktu kompilasi: function_types tidak muncul di sumber atau metadata.
Konversi
Dari function_typeF, ada konversi implisit function_type:
- Ke function_type
Gjika parameter dan jenis pengembalianFdapat dikonversi sesuai varians ke parameter dan jenis pengembalianG - Untuk
System.MulticastDelegateatau kelas dasar atau antarmukaSystem.MulticastDelegate - Untuk
System.Linq.Expressions.ExpressionatauSystem.Linq.Expressions.LambdaExpression
Ekspresi fungsi anonim dan grup metode sudah memiliki konversi dari ekspresi ke jenis delegasi dan jenis pohon ekspresi (lihat konversi fungsi anonim §10,7 dan konversi grup metode §10,8). Konversi tersebut cukup untuk mengonversi ke tipe delegasi bertipe kuat dan tipe pohon ekspresi. Konversi function_type di atas menambahkan konversi dari tipe ke tipe dasar saja: System.MulticastDelegate, System.Linq.Expressions.Expression, dll.
Tidak ada konversi ke function_type dari jenis selain function_type. Tidak ada konversi eksplisit untuk function_types karena function_types tidak dapat dirujuk dalam sumber.
Konversi ke System.MulticastDelegate atau jenis dasar atau antarmuka mewujudkan fungsi anonim atau grup metode sebagai instans jenis delegasi yang sesuai.
Konversi ke System.Linq.Expressions.Expression<TDelegate> atau jenis dasar mewujudkan ekspresi lambda sebagai pohon ekspresi dengan jenis delegasi yang sesuai.
Delegate d = delegate (object obj) { }; // Action<object>
Expression e = () => ""; // Expression<Func<string>>
object o = "".Clone; // Func<object>
Function_type konversi bukan konversi standar implisit atau eksplisit §10.4 dan tidak dipertimbangkan saat menentukan apakah operator konversi yang ditentukan pengguna berlaku untuk fungsi anonim atau grup metode. Dari evaluasi konversi yang ditentukan pengguna §10.5.3:
Agar operator konversi berlaku, harus dimungkinkan untuk melakukan konversi standar (§10,4) dari jenis sumber ke jenis operand operator, dan harus dimungkinkan untuk melakukan konversi standar dari jenis hasil operator ke jenis target.
class C
{
public static implicit operator C(Delegate d) { ... }
}
C c;
c = () => 1; // error: cannot convert lambda expression to type 'C'
c = (C)(() => 2); // error: cannot convert lambda expression to type 'C'
Sebuah peringatan dilaporkan untuk konversi implisit dari kelompok metode ke object, karena konversi tersebut valid, tetapi mungkin tidak disengaja.
Random r = new Random();
object obj;
obj = r.NextDouble; // warning: Converting method group to 'object'. Did you intend to invoke the method?
obj = (object)r.NextDouble; // ok
Inferensi jenis
Aturan yang ada untuk inferensi jenis sebagian besar tidak berubah (lihat §12.6.3). Namun, ada beberapa perubahan di bawah ini pada tahapan spesifik dalam inferensi tipe.
Fase pertama
Fase pertama (§12.6.3.2) memungkinkan fungsi anonim untuk mengikat Ti meskipun Ti bukan delegasi atau jenis pohon ekspresi (mungkin parameter jenis yang dibatasi untuk System.Delegate misalnya).
Untuk setiap argumen metode
Ei:
- Jika
Eiadalah fungsi anonim danTiadalah jenis delegasi atau jenis pohon ekspresi, inferensi jenis parameter eksplisit dibuat dariEikeTidan inferensi jenis pengembalian eksplisit dibuat dariEikeTi.- Jika tidak, jika
Eimemiliki jenisUdanxiadalah parameter nilai, maka inferensi terikat lebih rendah dilakukan dariUkeTi.- Jika tidak, jika
Eimemiliki jenisUdanxiadalah parameterrefatauoutmaka inferensi yang tepat dibuat dariUkeTi.- Jika tidak, tidak ada inferensi yang dibuat untuk argumen ini.
inferensi jenis pengembalian eksplisit
Sebuah inferensi tipe pengembalian eksplisit dibuat dari ekspresi
Eke tipeTdengan cara berikut:
- Jika
Eadalah fungsi anonim dengan jenis pengembalian eksplisitUrdanTadalah tipe delegasi atau jenis pohon ekspresi dengan jenis pengembalianVrmaka inferensi yang tepat (§12.6.3.9) dibuat dariUrkeVr.
Memperbaiki
Memperbaiki (§12.6.3.12) memastikan konversi lainnya lebih diutamakan daripada konversi function_type. (Ekspresi Lambda dan ekspresi grup metode hanya berkontribusi pada batas bawah sehingga penanganan function_types diperlukan hanya untuk batas yang lebih rendah.)
Variabel jenis yang belum diperbaiki
Xidengan serangkaian batas tetap sebagai berikut:
- Kumpulan jenis kandidat
Ujdimulai sebagai kumpulan semua jenis dalam kumpulan batas untukXidi mana jenis fungsi diabaikan dalam batas bawah jika ada jenis yang bukan jenis fungsi.- Kami kemudian memeriksa setiap batas untuk
Xisecara bergantian: Untuk setiap batas tepatUdariXi, semua jenisUjyang tidak identik denganUdihapus dari set kandidat. Untuk setiapUterikat yang lebih rendahXisemua jenisUjyang tidak konversi implisit dariUdihapus dari set kandidat. Untuk setiapUterikat atasXisemua jenisUjyang tidak konversi implisit keUdihapus dari set kandidat.- Jika di antara jenis kandidat yang tersisa
Ujada jenis unikVdari mana ada konversi implisit ke semua jenis kandidat lainnya, makaXidiperbaiki untukV.- Jika tidak, penentuan jenis gagal.
Tipe umum yang terbaik
Jenis umum terbaik (§12.6.3.15) didefinisikan dalam hal inferensi jenis sehingga perubahan inferensi jenis di atas juga berlaku untuk jenis umum terbaik.
var fs = new[] { (string s) => s.Length, (string s) => int.Parse(s) }; // Func<string, int>[]
var
Fungsi anonim dan grup metode dengan jenis fungsi dapat digunakan sebagai inisialisasi dalam deklarasi var.
var f1 = () => default; // error: cannot infer type
var f2 = x => x; // error: cannot infer type
var f3 = () => 1; // System.Func<int>
var f4 = string () => null; // System.Func<string>
var f5 = delegate (object o) { }; // System.Action<object>
static void F1() { }
static void F1<T>(this T t) { }
static void F2(this string s) { }
var f6 = F1; // error: multiple methods
var f7 = "".F1; // error: the delegate type could not be inferred
var f8 = F2; // System.Action<string>
Jenis fungsi tidak digunakan dalam penugasan untuk membuang.
d = () => 0; // ok
_ = () => 1; // error
Jenis Delegasi
Jenis delegasi untuk fungsi anonim atau grup metode dengan jenis parameter P1, ..., Pn dan jenis pengembalian R adalah:
- jika ada parameter atau nilai yang dikembalikan bukan berdasarkan nilai, atau ada lebih dari 16 parameter, atau salah satu jenis parameter atau pengembalian bukan argumen jenis yang valid (misalnya,
(int* p) => { }), maka delegasi adalah jenis delegasiinternalanonim yang disintesis dengan tanda tangan yang cocok dengan fungsi anonim atau grup metode, dan dengan nama parameterarg1, ..., argnatauargjika satu parameter; - jika
Radalahvoid, maka jenis delegasi adalahSystem.Action<P1, ..., Pn>; - jika tidak, tipe delegasi adalah
System.Func<P1, ..., Pn, R>.
Pengkompilasi mungkin akan mengizinkan lebih banyak signature untuk terhubung ke jenis System.Action<> dan System.Func<> di masa mendatang (misalnya jika jenis ref struct diizinkan sebagai argumen tipe).
modopt() atau modreq() dalam penandaan grup metode diabaikan pada tipe delegasi yang bersesuaian.
Jika dua fungsi anonim atau grup metode dalam kompilasi yang sama memerlukan jenis delegasi yang disintesis dengan jenis parameter dan pengubah yang sama dan jenis pengembalian dan pengubah yang sama, pengompilasi akan menggunakan jenis delegasi yang disintesis yang sama.
Resolusi kelebihan beban
Anggota fungsi yang lebih baik (§12.6.4.3) diperbarui untuk lebih memilih anggota di mana tidak ada konversi maupun argumen tipe yang melibatkan tipe yang disimpulkan dari ekspresi lambda atau grup metode.
Anggota fungsi yang lebih baik
... Mengingat daftar argumen
Adengan sekumpulan ekspresi argumen{E1, E2, ..., En}dan dua anggota fungsi yang berlakuMpdanMqdengan jenis parameter{P1, P2, ..., Pn}dan{Q1, Q2, ..., Qn},Mpdidefinisikan menjadi anggota fungsi lebih baik daripadaMqjika
- untuk setiap argumen, konversi implisit dari
ExkePxbukan function_type_conversion, dan
Mpadalah metode non-generik atauMpadalah metode generik dengan parameter jenis{X1, X2, ..., Xp}dan untuk setiap parameter jenisXiargumen jenis disimpulkan dari ekspresi atau dari jenis selain function_type, dan- untuk setidaknya satu argumen, konversi implisit dari
ExkeQxadalah function_type_conversion, atauMqadalah metode generik dengan parameter jenis{Y1, Y2, ..., Yq}dan untuk setidaknya satu parameter jenisYiargumen jenis disimpulkan dari function_type, atau- untuk setiap argumen, konversi implisit dari
ExkeQxtidak lebih baik daripada konversi implisit dariExkePx, dan setidaknya untuk satu argumen, konversi dariExkePxlebih baik daripada konversi dariExkeQx.
Konversi yang lebih baik dari ekspresi (§12.6.4.5) diperbarui untuk lebih memilih konversi yang tidak melibatkan jenis yang disimpulkan dari ekspresi lambda atau grup metode.
Konversi yang lebih baik dari ekspresi
Diberikan konversi implisit
C1yang mengonversi dari ekspresiEke tipeT1, dan konversi implisitC2yang mengonversi dari ekspresiEke tipeT2,C1adalah konversi yang lebih baik daripadaC2jika:
C1bukan function_type_conversion danC2adalah function_type_conversion, atauEadalah interpolated_string_expressionnon-konstan,C1adalah implicit_string_handler_conversion,T1adalah applicable_interpolated_string_handler_type, danC2bukan implicit_string_handler_conversion, atauEtidak persis sama denganT2dan setidaknya salah satu dari kondisi berikut terpenuhi:
Sintaksis
lambda_expression
: modifier* identifier '=>' (block | expression)
| attribute_list* modifier* type? lambda_parameters '=>' (block | expression)
;
lambda_parameters
: lambda_parameter
| '(' (lambda_parameter (',' lambda_parameter)*)? ')'
;
lambda_parameter
: identifier
| attribute_list* modifier* type? identifier equals_value_clause?
;
Masalah terbuka
Haruskah nilai default didukung untuk parameter ekspresi lambda untuk kelengkapan?
Haruskah System.Diagnostics.ConditionalAttribute dilarang pada ekspresi lambda karena ada beberapa skenario di mana ekspresi lambda dapat digunakan secara kondisional?
([Conditional("DEBUG")] static (x, y) => Assert(x == y))(a, b); // ok?
Haruskah function_type tersedia dari API pengkompilasi, selain jenis delegasi yang dihasilkan?
Saat ini, tipe delegasi yang diinferensi menggunakan System.Action<> atau System.Func<> ketika parameter dan jenis pengembalian adalah argumen tipe yang valid dan, serta tidak lebih dari 16 parameter, dan jika tipe Action<> atau Func<> yang diharapkan tidak ada, akan dilaporkan kesalahan. Sebagai gantinya, apakah pengompilasi harus menggunakan System.Action<> atau System.Func<> terlepas dari aritasnya? Dan jika jenis yang diharapkan hilang, sintesislah jenis delegasi sebagai gantinya?
C# feature specifications