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 tercatat 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 juara: https://github.com/dotnet/csharplang/issues/184
Ringkasan
Proposal ini menambahkan kemampuan untuk menulis literal string UTF8 di C# dan membuatnya secara otomatis dikodekan ke dalam representasi byte
UTF-8 mereka.
Motivasi
UTF8 adalah bahasa web dan penggunaannya diperlukan dalam bagian signifikan dari tumpukan .NET. Sementara banyak data datang dalam bentuk byte[]
dari tumpukan jaringan masih ada penggunaan konstanta yang signifikan dalam kode. Misalnya, tumpukan jaringan sering kali harus menuliskan konstanta seperti "HTTP/1.0\r\n"
, " AUTH"
, atau.
"Content-Length: "
.
Saat ini tidak ada sintaks yang efisien untuk melakukan ini karena C# mewakili semua string menggunakan pengodean UTF16. Itu berarti pengembang harus memilih antara kenyamanan pengodean pada runtime yang menimbulkan overhead, termasuk waktu yang dihabiskan saat startup benar-benar melakukan operasi pengodean (dan alokasi jika menargetkan jenis yang tidak benar-benar memerlukannya), atau secara manual menerjemahkan byte dan menyimpan dalam byte[]
.
// Efficient but verbose and error prone
static ReadOnlySpan<byte> AuthWithTrailingSpace => new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
WriteBytes(AuthWithTrailingSpace);
// Incurs allocation and startup costs performing an encoding that could have been done at compile-time
static readonly byte[] s_authWithTrailingSpace = Encoding.UTF8.GetBytes("AUTH ");
WriteBytes(s_authWithTrailingSpace);
// Simplest / most convenient but terribly inefficient
WriteBytes(Encoding.UTF8.GetBytes("AUTH "));
Kompromi ini adalah masalah utama yang sering muncul bagi para mitra kami di runtime, ASP.NET, dan Azure. Sering kali hal itu menyebabkan mereka tidak memanfaatkan performa sepenuhnya karena mereka tidak ingin menghadapi kerepotan menulis kode byte[]
dengan tangan.
Untuk memperbaikinya, kami akan memungkinkan penggunaan literal UTF8 dalam bahasa dan mengonversinya menjadi UTF8 byte[]
pada saat kompilasi.
Desain terperinci
akhiran u8
untuk literal string
Bahasa ini akan memberikan akhiran u8
pada literal string untuk memaksa jenis menjadi UTF8.
Akhiran tidak peka huruf besar/kecil, akhiran U8
akan didukung dan akan memiliki arti yang sama dengan akhiran u8
.
Ketika akhiran u8
digunakan, nilai literal adalah ReadOnlySpan<byte>
yang berisi representasi byte UTF-8 dari string.
Penyudah null ditempatkan di luar byte terakhir dalam memori (dan di luar panjang ReadOnlySpan<byte>
) untuk menangani beberapa skenario interop di mana panggilan mengharapkan string yang diakhiri dengan null.
string s1 = "hello"u8; // Error
var s2 = "hello"u8; // Okay and type is ReadOnlySpan<byte>
ReadOnlySpan<byte> s3 = "hello"u8; // Okay.
byte[] s4 = "hello"u8; // Error - Cannot implicitly convert type 'System.ReadOnlySpan<byte>' to 'byte[]'.
byte[] s5 = "hello"u8.ToArray(); // Okay.
Span<byte> s6 = "hello"u8; // Error - Cannot implicitly convert type 'System.ReadOnlySpan<byte>' to 'System.Span<byte>'.
Karena literal akan dialokasikan sebagai konstanta global, masa hidup ReadOnlySpan<byte>
yang dihasilkan tidak akan mencegahnya untuk dikembalikan atau dioper ke tempat lain. Namun, dalam konteks tertentu, terutama dalam fungsi asinkron, tipe struct ref lokal tidak diizinkan, sehingga hal ini menyebabkan penalti penggunaan dalam situasi tersebut, dengan diperlukan panggilan ToArray()
atau yang serupa.
Harfiah u8
tidak memiliki nilai konstanta. Itu karena ReadOnlySpan<byte>
tidak dapat menjadi jenis konstanta hari ini. Jika definisi const
diperluas di masa mendatang untuk mempertimbangkan ReadOnlySpan<byte>
, maka nilai ini juga harus dianggap sebagai konstanta. Dalam praktiknya, ini berarti literal u8
tidak dapat digunakan sebagai nilai default dari parameter opsional.
// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing"u8) { ... }
Ketika teks input untuk literal adalah string UTF16 yang salah bentuk, maka bahasa akan memancarkan kesalahan:
var bytes = "hello \uD8\uD8"u8; // Error: malformed UTF16 input string
var bytes2 = "hello \uD801\uD802"u8; // Allowed: invalid UTF16 values, but it's correctly formed.
Operator penambahan
Sebuah poin baru akan ditambahkan ke §12.10.5 operator tambahan sebagai berikut.
Perangkaian representasi byte UTF-8:
ReadOnlySpan<byte> operator +(ReadOnlySpan<byte> x, ReadOnlySpan<byte> y);
Operator
+
biner ini melakukan perangkaian urutan byte dan berlaku jika dan hanya jika kedua operan secara semantik representasi byte UTF8. Operan secara semantik adalah representasi byte UTF8 ketika merupakan nilai literalu8
, atau nilai yang dihasilkan oleh operator penggabungan representasi byte UTF8.Hasil dari perangkaian representasi byte UTF8 adalah
ReadOnlySpan<byte>
yang terdiri dari byte operand kiri diikuti oleh byte operand kanan. Penyudah null ditempatkan di luar byte terakhir dalam memori (dan di luar panjangReadOnlySpan<byte>
) untuk menangani beberapa skenario interop di mana panggilan mengharapkan string yang diakhiri dengan null.
Menurunkan
Bahasa akan menurunkan string yang dikodekan UTF8 persis seolah-olah pengembang telah mengetik byte[]
yang dihasilkan secara harfiah dalam kode. Misalnya:
ReadOnlySpan<byte> span = "hello"u8;
// Equivalent to
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00 }).
Slice(0,5); // The `Slice` call will be optimized away by the compiler.
Itu berarti semua pengoptimalan yang berlaku untuk formulir new byte[] { ... }
juga akan berlaku untuk literal utf8. Ini berarti situs panggilan akan bebas alokasi karena C# akan mengoptimalkan ini untuk disimpan di bagian .data
file PE.
Beberapa penerapan berturut-turut dari operator penggabungan representasi byte UTF8 digabungkan menjadi satu pembuatan ReadOnlySpan<byte>
dengan array byte yang berisi urutan byte terakhir.
ReadOnlySpan<byte> span = "h"u8 + "el"u8 + "lo"u8;
// Equivalent to
ReadOnlySpan<byte> span = new ReadOnlySpan<byte>(new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x00 }).
Slice(0,5); // The `Slice` call will be optimized away by the compiler.
Kekurangan
Mengandalkan API inti
Implementasi kompilator akan menggunakan UTF8Encoding
untuk deteksi string yang tidak valid serta terjemahan ke byte[]
. API yang tepat mungkin akan bergantung pada kerangka kerja target mana yang digunakan kompilator. Tetapi UTF8Encoding
akan menjadi tulang punggung dari implementasi.
Secara historis pengkompilasi telah menghindari penggunaan API runtime untuk pemrosesan harfiah. Itu karena mengambil kontrol tentang bagaimana konstanta diproses jauh dari bahasa dan ke dalam runtime. Secara konkret itu berarti item seperti perbaikan bug dapat mengubah pengodean konstan dan berarti bahwa hasil kompilasi C# tergantung pada runtime mana yang dijalankan kompilator.
Ini bukan masalah hipotetis. Versi awal Roslyn menggunakan double.Parse
untuk menangani penguraian konstanta bilangan floating-point. Itu menyebabkan sejumlah masalah. Pertama berarti bahwa beberapa nilai titik mengambang memiliki representasi yang berbeda antara kompilator asli dan Roslyn. Kedua, seiring dengan evolusi .NET Core dan perbaikan bug yang sudah lama ada dalam kode double.Parse
, arti dari konstanta tersebut berubah dalam bahasa tergantung pada runtime apa yang dijalankan oleh pengompilasi. Akibatnya pengkompilasi akhirnya menulis versi kode penguraian titik pecahannya sendiri dan menghapus dependensi pada double.Parse
.
Skenario ini dibahas dengan tim runtime dan kami tidak merasa memiliki masalah yang sama dengan yang pernah kami alami sebelumnya. Penguraian UTF8 stabil di seluruh runtime dan tidak ada masalah yang diketahui di area ini yang merupakan kekhawatiran kompatibilitas di masa depan. Jika ada yang muncul, kita bisa mengevaluasi kembali strateginya.
Alternatif
Jenis target saja
Desain dapat bergantung hanya pada pengetikan target dan menghapus akhiran u8
pada literal string
. Dalam sebagian besar kasus saat ini string
harfiah ditugaskan langsung ke ReadOnlySpan<byte>
oleh karena itu tidak perlu.
ReadOnlySpan<byte> span = "Hello World;"
Akhiran u8
terutama ada untuk mendukung dua skenario: var
dan resolusi kelebihan beban. Untuk yang terakhir, pertimbangkan kasus penggunaan berikut:
void Write(ReadOnlySpan<byte> span) { ... }
void Write(string s) {
var bytes = Encoding.UTF8.GetBytes(s);
Write(bytes.AsSpan());
}
Mengacu pada implementasinya, lebih baik memanggil Write(ReadOnlySpan<byte>)
, dan akhiran u8
membuatnya lebih nyaman: Write("hello"u8)
. Karena tidak adanya opsi, pengembang perlu menggunakan casting yang canggung Write((ReadOnlySpan<byte>)"hello")
.
Namun, ini adalah fitur kenyamanan, fitur tersebut dapat ada tanpanya dan tidak masalah untuk menambahkannya di waktu yang akan datang.
Tunggu jenis Utf8String
Sementara ekosistem .NET menstandarkan pada ReadOnlySpan<byte>
sebagai jenis string defacto Utf8 hari ini, ada kemungkinan runtime akan memperkenalkan jenis Utf8String
aktual di masa depan.
Kita harus mengevaluasi desain kita di sini dalam menghadapi kemungkinan perubahan ini dan merefleksikan apakah kita akan menyesali keputusan yang telah kita buat. Ini harus ditimbang dibandingkan dengan kemungkinan realistis bahwa kita mungkin akan memperkenalkan Utf8String
, kemungkinan yang tampaknya berkurang setiap hari kita menemukan ReadOnlySpan<byte>
sebagai alternatif yang dapat diterima.
Tampaknya tidak mungkin kita akan menyesali konversi tipe target antara literal string dan ReadOnlySpan<byte>
. Penggunaan ReadOnlySpan<byte>
sebagai utf8 disematkan di API kami sekarang dan karenanya masih ada nilai dalam konversi bahkan jika Utf8String
datang dan merupakan jenis "lebih baik". Bahasa ini mungkin saja lebih memilih untuk konversi ke Utf8String
daripada ReadOnlySpan<byte>
.
Kemungkinan besar kita akan menyesal bahwa akhiran u8
menunjuk ke ReadOnlySpan<byte>
alih-alih Utf8String
. Ini akan mirip dengan betapa kita menyesali bahwa stackalloc int[]
memiliki tipe alami int*
daripada Span<int>
. Ini bukan masalah besar, hanya ketidaknyamanan.
Konversi antara konstanta string
dan urutan byte
Konversi di bagian ini belum diimplementasikan. Konversi ini tetap sebagai usulan yang aktif.
Bahasa ini akan memungkinkan konversi antara konstanta string
dan urutan byte
di mana teks dikonversi menjadi representasi byte UTF8 yang setara. Secara khusus, kompilator akan memungkinkan string_constant_to_UTF8_byte_representation_conversion - konversi implisit dari konstanta string
ke byte[]
, Span<byte>
, dan ReadOnlySpan<byte>
.
Poin baru akan ditambahkan ke bagian konversi implisit §10.2. Konversi ini bukan konversi standar §10,4.
byte[] array = "hello"; // new byte[] { 0x68, 0x65, 0x6c, 0x6c, 0x6f }
Span<byte> span = "dog"; // new byte[] { 0x64, 0x6f, 0x67 }
ReadOnlySpan<byte> span = "cat"; // new byte[] { 0x63, 0x61, 0x74 }
Ketika teks input untuk konversi adalah string UTF16 yang salah bentuk, bahasa akan mengeluarkan kesalahan:
const string text = "hello \uD801\uD802";
byte[] bytes = text; // Error: the input string is not valid UTF16
Penggunaan utama dari fitur ini diharapkan dengan literal, tetapi akan berfungsi dengan nilai konstanta string
apa pun.
Konversi dari konstanta string
dengan nilai null
juga akan didukung. Hasil konversi akan menjadi nilai default
dari tipe target.
const string data = "dog"
ReadOnlySpan<byte> span = data; // new byte[] { 0x64, 0x6f, 0x67 }
Dalam kasus operasi konstan pada string, seperti +
, pengodean ke UTF8 akan terjadi pada tahap akhir string
, berbeda dengan yang seharusnya terjadi pada bagian-bagian individu lalu menggabungkan hasilnya. Pemesanan ini penting untuk dipertimbangkan karena dapat berdampak pada apakah konversi berhasil atau tidak.
const string first = "\uD83D"; // high surrogate
const string second = "\uDE00"; // low surrogate
ReadOnlySpan<byte> span = first + second;
Dua bagian di sini tidak valid karena bagian-bagian tersebut tidak lengkap dari pasangan pengganti. Secara individual tidak ada terjemahan yang benar ke UTF8 tetapi bersama-sama mereka membentuk pasangan pengganti lengkap yang dapat berhasil diterjemahkan ke UTF8.
string_constant_to_UTF8_byte_representation_conversion tidak diizinkan di Pohon Ekspresi Linq.
Meskipun input ke konversi ini adalah konstanta dan data sepenuhnya dikodekan pada waktu kompilasi, konversi tidak dianggap konstan oleh bahasa. Itu karena array tidak konstan hari ini. Jika definisi const
diperluas di masa mendatang untuk mempertimbangkan array, konversi ini juga harus dipertimbangkan. Praktis meskipun ini berarti hasil dari konversi ini tidak dapat digunakan sebagai nilai default parameter opsional.
// Error: The argument is not constant
void Write(ReadOnlySpan<byte> message = "missing") { ... }
Setelah diimplementasikan, literal string akan memiliki masalah yang sama dengan yang dimiliki harfiah lain dalam bahasa: jenis apa yang mereka wakili tergantung pada bagaimana mereka digunakan. C# memberikan akhiran literal untuk membedakan arti dari literal lain. Misalnya pengembang dapat menulis 3.14f
untuk memaksa nilai menjadi float
atau 1l
untuk memaksa nilai menjadi long
.
Pertanyaan yang belum terselesaikan
Tiga pertanyaan desain pertama berkaitan dengan string ke konversi Span<byte>
/ ReadOnlySpan<byte>
. Mereka belum diimplementasikan.
(Diselesaikan) Konversi antara konstanta string
dengan nilai null
dan urutan byte
Apakah konversi ini didukung dan, jika demikian, bagaimana hal itu dilakukan tidak ditentukan.
Proposal :
Izinkan konversi implisit dari konstanta string
dengan nilai null
ke byte[]
, Span<byte>
, dan ReadOnlySpan<byte>
. Hasil konversi adalah nilai default
dari jenis target.
Resolusi :
Usulan disetujui - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversions-from-null-literals.
(Diselesaikan) Di manakah string_constant_to_UTF8_byte_representation_conversion seharusnya berada?
Apakah string_constant_to_UTF8_byte_representation_conversion merupakan poin dalam bagian konversi implisit §10.2 tersendiri, atau apakah itu bagian dari §10.2.11, atau apakah itu termasuk dalam kelompok lain dalam konversi implisit yang sudah ada?
Proposal :
Ini adalah poin baru dalam konversi implisit §10.2, mirip dengan "Konversi string terinterpolasi implisit" atau "Konversi grup metode". Sepertinya tidak termasuk dalam "Konversi Ekspresi Konstan Implisit" karena, meskipun sumbernya adalah ekspresi konstan, hasilnya tidak pernah merupakan ekspresi konstan. Selain itu, "Konversi ekspresi konstanta implisit" dianggap sebagai "Konversi implisit standar" §10.4.2, yang kemungkinan menyebabkan perubahan perilaku non-sepele yang melibatkan konversi yang ditentukan pengguna.
Resolusi :
Kami akan memperkenalkan jenis konversi baru untuk konstanta string ke UTF-8 byte - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-kinds
(Diselesaikan) Apakah string_constant_to_UTF8_byte_representation_conversion sebuah konversi standar
Selain Konversi Standar "murni" (konversi standar adalah konversi yang telah ditentukan sebelumnya yang dapat terjadi sebagai bagian dari konversi yang ditentukan pengguna), pengkompilasi juga memperlakukan beberapa konversi yang telah ditentukan sebelumnya sebagai standar "agak". Misalnya, konversi string terinterpolasi implisit dapat terjadi sebagai bagian dari konversi yang ditentukan pengguna jika ada transmisi eksplisit ke jenis target dalam kode. Seolah-olah itu adalah Konversi Eksplisit Standar, meskipun itu adalah konversi implisit yang tidak secara eksplisit disertakan ke dalam kumpulan konversi implisit atau eksplisit standar. Misalnya:
class C
{
static void Main()
{
C1 x = $"hello"; // error CS0266: Cannot implicitly convert type 'string' to 'C1'. An explicit conversion exists (are you missing a cast?)
var y = (C1)$"dog"; // works
}
}
class C1
{
public static implicit operator C1(System.FormattableString x) => new C1();
}
Proposal :
Konversi baru bukan konversi standar. Ini akan menghindari perubahan perilaku non-sepele yang melibatkan konversi yang ditentukan pengguna. Misalnya, kita tidak perlu khawatir tentang cinversion yang ditentukan pengguna di bawah konversi literal tuple implisit, dll.
Resolusi :
Bukan konversi standar, untuk saat ini - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#implicit-standard-conversion.
(Diselesaikan) Konversi Pohon Ekspresi Linq
Haruskah string_constant_to_UTF8_byte_representation_conversion diizinkan dalam konteks konversi Pohon Ekspresi Linq? Kita dapat menolaknya untuk saat ini, atau kita dapat memasukkan bentuk "rendah" ke dalam pohon. Misalnya:
Expression<Func<byte[]>> x = () => "hello"; // () => new [] {104, 101, 108, 108, 111}
Expression<FuncSpanOfByte> y = () => "dog"; // () => new Span`1(new [] {100, 111, 103})
Expression<FuncReadOnlySpanOfByte> z = () => "cat"; // () => new ReadOnlySpan`1(new [] {99, 97, 116})
Bagaimana dengan literal string yang memiliki akhiran u8
? Kita dapat menampilkannya sebagai pembuatan array byte:
Expression<Func<byte[]>> x = () => "hello"u8; // () => new [] {104, 101, 108, 108, 111}
Resolusi :
Tidak diizinkan dalam Pohon Ekspresi Linq - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#expression-tree-representation.
(Diselesaikan) Tipe alami dari string literal dengan akhiran u8
Bagian "Desain terperinci" mengatakan: "Jenis alami, bagaimanapun, akan menjadi ReadOnlySpan<byte>
." Pada saat yang sama: "Ketika akhiran u8
digunakan, literal tetap dapat dikonversi ke salah satu jenis yang diizinkan: byte[]
, Span<byte>
, atau ReadOnlySpan<byte>
."
Ada beberapa kelemahan dengan pendekatan ini:
-
ReadOnlySpan<byte>
tidak tersedia pada kerangka kerja desktop; - Tidak ada konversi yang ada dari
ReadOnlySpan<byte>
kebyte[]
atauSpan<byte>
. Untuk mendukung mereka, kita mungkin perlu memperlakukan literal sebagai tipe target. Aturan bahasa dan implementasi akan menjadi lebih rumit.
Proposal :
Jenis alami adalah byte[]
. Ini sudah tersedia di semua kerangka kerja. Ngomong-ngomong, pada saat runtime, kami akan selalu memulai dengan membuat array byte, bahkan dengan usulan asli. Kami juga tidak memerlukan aturan konversi khusus untuk mendukung konversi ke Span<byte>
dan ReadOnlySpan<byte>
. Sudah ada konversi implisit yang ditentukan pengguna dari byte[]
ke Span<byte>
dan ReadOnlySpan<byte>
. Bahkan ada konversi implisit yang ditentukan pengguna ke ReadOnlyMemory<byte>
(lihat pertanyaan "Kedalaman konversi" di bawah). Ada satu kekurangan, bahasa tidak memungkinkan rantai konversi yang didefinisikan pengguna. Jadi, kode berikut tidak akan dikompilasi:
using System;
class C
{
static void Main()
{
var y = (C2)"dog"u8; // error CS0030: Cannot convert type 'byte[]' to 'C2'
var z = (C3)"cat"u8; // error CS0030: Cannot convert type 'byte[]' to 'C3'
}
}
class C2
{
public static implicit operator C2(Span<byte> x) => new C2();
}
class C3
{
public static explicit operator C3(ReadOnlySpan<byte> x) => new C3();
}
Namun, seperti halnya konversi yang ditentukan pengguna, pemeran eksplisit dapat digunakan untuk membuat satu konversi yang ditentukan pengguna sebagai bagian dari konversi lain yang ditentukan pengguna.
Rasanya semua skenario motivasi dapat diatasi dengan byte[]
sebagai tipe alami, yang mana akan membuat aturan dan implementasi bahasa menjadi lebih sederhana.
Resolusi :
Usulan disetujui - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#natural-type-of-u8-literals.
Kami mungkin ingin membahas lebih dalam apakah string literal u8
harus memiliki tipe array mutabel, tetapi kami tidak berpikir bahwa perdebatan itu diperlukan saat ini.
Hanya operator konversi eksplisit yang telah diterapkan.
(Diselesaikan) Kedalaman konversi
Apakah itu juga dapat berfungsi di mana saja di mana byte[] dapat berfungsi? Anggap:
static readonly ReadOnlyMemory<byte> s_data1 = "Data"u8;
static readonly ReadOnlyMemory<byte> s_data2 = "Data";
Contoh pertama kemungkinan besar akan berfungsi karena jenis alami yang berasal dari u8
.
Contoh kedua sulit untuk bekerja karena membutuhkan konversi di kedua arah. Itu kecuali kita menambahkan ReadOnlyMemory<byte>
sebagai salah satu jenis konversi yang diizinkan.
Proposal :
Jangan melakukan sesuatu yang istimewa.
Resolusi :
Tidak ada target konversi baru yang ditambahkan untuk saat ini https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#conversion-depth. Tidak satupun konversi berhasil dikompilasi.
(Diselesaikan) Pemecahan masalah fungsi kelebihan beban rusak
API berikut akan menjadi ambigu:
M("");
static void M1(ReadOnlySpan<char> charArray) => ...;
static void M1(byte[] byteArray) => ...;
Apa yang harus kita lakukan untuk mengatasi hal ini?
Proposal :
Mirip dengan https://github.com/dotnet/csharplang/blob/main/proposals/csharp-10.0/lambda-improvements.md#overload-resolution, anggota fungsi yang lebih baik (§11.6.4.3) diperbarui untuk lebih memilih anggota di mana konversi yang terlibat tidak memerlukan perubahan konstanta string
ke urutan UTF8 byte
.
Anggota fungsi yang lebih baik
... Mengingat daftar argumen
A
dengan sekumpulan ekspresi argumen{E1, E2, ..., En}
dan dua anggota fungsi yang berlakuMp
danMq
dengan jenis parameter{P1, P2, ..., Pn}
dan{Q1, Q2, ..., Qn}
,Mp
didefinisikan menjadi anggota fungsi lebih baik daripadaMq
jika
- untuk setiap argumen, konversi implisit dari
Ex
kePx
bukan string_constant_to_UTF8_byte_representation_conversion, dan setidaknya untuk satu argumen, konversi implisit dariEx
keQx
adalah string_constant_to_UTF8_byte_representation_conversion, atau- untuk setiap argumen, konversi implisit dari
Ex
kePx
bukan function_type_conversion, dan
Mp
adalah metode non-generik atauMp
adalah metode generik dengan parameter tipe{X1, X2, ..., Xp}
dan untuk setiap parameter tipeXi
, argumen tipe disimpulkan dari sebuah ekspresi atau dari tipe selain function_type, dan- untuk setidaknya satu argumen, konversi implisit dari
Ex
keQx
adalah function_type_conversion, atauMq
adalah metode generik dengan parameter jenis{Y1, Y2, ..., Yq}
dan untuk setidaknya satu parameter jenisYi
argumen jenis disimpulkan dari function_type, atau- untuk setiap argumen, konversi implisit dari
Ex
keQx
tidak lebih baik daripada konversi implisit dariEx
kePx
, dan setidaknya untuk satu argumen, konversi dariEx
kePx
lebih baik daripada konversi dariEx
keQx
.
Perhatikan bahwa penambahan aturan ini tidak akan mencakup skenario di mana metode instans berlaku dan "mengungguli" metode ekstensi. Misalnya:
using System;
class Program
{
static void Main()
{
var p = new Program();
Console.WriteLine(p.M(""));
}
public string M(byte[] b) => "byte[]";
}
static class E
{
public static string M(this object o, string s) => "string";
}
Perilaku kode ini akan berubah secara diam-diam dari mencetak "string" menjadi mencetak "byte[]".
Apakah kita Ok dengan perubahan perilaku ini? Haruskah didokumenkan sebagai perubahan yang melanggar?
Perhatikan bahwa tidak ada proposal untuk membuat string_constant_to_UTF8_byte_representation_conversion menjadi tidak tersedia ketika versi bahasa C#10 ditargetkan. Dalam kasus ini, contoh di atas menjadi kesalahan alih-alih kembali ke perilaku C#10. Ini mengikuti prinsip umum bahwa versi bahasa target tidak memengaruhi semantik bahasa.
Apakah kita Baik-baik saja dengan perilaku ini? Haruskah didokumenkan sebagai perubahan yang melanggar?
Aturan baru juga tidak akan mencegah jeda yang terjadi pada konversi literal tuple. Misalnya
class C
{
static void Main()
{
System.Console.Write(Test(("s", 1)));
}
static string Test((object, int) a) => "object";
static string Test((byte[], int) a) => "array";
}
akan diam-diam mencetak "array" alih-alih "objek".
Apakah kita Baik-baik saja dengan perilaku ini? Haruskah didokumenkan sebagai perubahan yang melanggar? Mungkin kita bisa membuat aturan baru menjadi lebih kompleks untuk meneliti konversi literal tuple.
Resolusi :
Prototipe tidak akan menyesuaikan aturan apa pun di sini, sehingga kita mudah-mudahan dapat melihat apa yang melanggar dalam praktik - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#breaking-changes.
(Diselesaikan) Haruskah akhiran u8
tidak peka huruf besar/kecil?
Proposal :
Mendukung akhiran U8
juga demi konsistensi dengan akhiran numerik.
Resolusi :
Disetujui - https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md#suffix-case-sensitivity.
Contoh hari ini
Contoh di mana runtime telah mengodekan byte UTF8 secara manual hari ini
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/Common/src/System/Net/Http/aspnetcore/Http2/Hpack/StatusCodes.cs#L13-L78
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Memory/src/System/Buffers/Text/Base64Encoder.cs#L581-L591
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.HttpListener/src/System/Net/Windows/HttpResponseStream.Windows.cs#L284
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Stream.cs#L30
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http3RequestStream.cs#L852
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Text.Json/src/System/Text/Json/JsonConstants.cs#L35-L42
Contoh di mana kita meninggalkan perf pada tabel
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Security/src/System/Net/Security/Pal.Managed/SafeChannelBindingHandle.cs#L16-L17
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/HttpConnection.cs#L37-L43
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/Http2Connection.cs#L78
- https://github.com/dotnet/runtime/blob/e095fde94baa480a6d65dfdee43d9cc0ad0d0b38/src/libraries/System.Net.Mail/src/System/Net/Mail/SmtpCommands.cs#L669-L687
Rapat desain
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-01-26.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-04-18.md https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-06-06.md
C# feature specifications