Bagikan melalui


Memutus perubahan di Roslyn setelah .NET 6.0.100 hingga .NET 7.0.100

Dokumen ini mencantumkan perubahan mencolok yang diketahui di Roslyn setelah rilis umum .NET 6 (.NET SDK versi 6.0.100) melalui rilis umum .NET 7 (.NET SDK versi 7.0.100).

Semua variabel lokal dari tipe terbatas tidak diizinkan dalam metode asinkron

Diperkenalkan di Visual Studio 2022 versi 17.6p1

Lokal tipe terbatas tidak diizinkan dalam metode asinkron. Tetapi pada versi sebelumnya, pengkompilasi gagal melihat beberapa lokal yang dideklarasikan secara implisit. Misalnya, dalam pernyataan foreach atau using atau analisis mendalam.
Sekarang, variabel lokal yang dideklarasikan secara implisit juga tidak diperbolehkan.

ref struct RefStruct { public void Dispose() { } }
public class C 
{
    public async Task M() 
    {
        RefStruct local = default; // disallowed
        using (default(RefStruct)) { } // now disallowed too ("error CS9104: A using statement resource of this type cannot be used in async methods or async lambda expressions")
    }
}

Lihat https://github.com/dotnet/roslyn/pull/66264

Penunjuk harus selalu dalam konteks yang tidak aman.

Diperkenalkan di Visual Studio 2022 versi 17.6

Di SDK sebelumnya, kompilator kadang-kadang akan mengizinkan lokasi di mana penunjuk dapat dirujuk, tanpa secara eksplisit menandai lokasi tersebut sebagai tidak aman. Sekarang, pengubah unsafe harus ada.
Misalnya using Alias = List<int*[]>; harus diubah menjadi using unsafe Alias = List<int*[]>; legal.
Penggunaan seperti void Method(Alias a) ... harus diubah menjadi unsafe void Method(Alias a) ....

Aturan ini tidak bersyarat, kecuali untuk using deklarasi alias (yang tidak mengizinkan pengubah unsafe sebelum C# 12).
Jadi untuk using deklarasi, aturan hanya berlaku jika versi bahasa dipilih sebagai C# 12 atau lebih tinggi.

System.TypedReference dianggap terkelola

Diperkenalkan di Visual Studio 2022 versi 17.6

Di masa mendatang, jenis System.TypedReference ini akan dianggap sudah dikelola.

unsafe
{
    TypedReference* r = null; // warning: This takes the address of, gets the size of, or declares a pointer to a managed type
    var a = stackalloc TypedReference[1]; // error: Cannot take the address of, get the size of, or declare a pointer to a managed type
}

Kesalahan keamanan referensi tidak memengaruhi konversi dari ekspresi lambda ke deleget

Diperkenalkan di Visual Studio 2022 versi 17.5

Kesalahan keamanan ref yang dilaporkan dalam tubuh lambda tidak lagi memengaruhi konvertibilitas ekspresi lambda ke jenis delegasi. Perubahan ini dapat memengaruhi resolusi kelebihan beban.

Dalam contoh di bawah ini, panggilan ke M(x => ...) bersifat ambigu dengan Visual Studio 17.5 karena keduanya M(D1) dan M(D2) sekarang dianggap berlaku, meskipun panggilan ke F(ref x, ref y) dalam tubuh lambda akan mengakibatkan keamanan ref dengan M(D1) (lihat contoh di d1 dan d2 untuk perbandingan). Sebelumnya, panggilan terikat dengan jelas ke M(D2) karena M(D1) overload dianggap tidak sesuai.

using System;

ref struct R { }

delegate R D1(R r);
delegate object D2(object o);

class Program
{
    static void M(D1 d1) { }
    static void M(D2 d2) { }

    static void F(ref R x, ref Span<int> y) { }
    static void F(ref object x, ref Span<int> y) { }

    static void Main()
    {
        // error CS0121: ambiguous between: 'M(D1)' and 'M(D2)'
        M(x =>
            {
                Span<int> y = stackalloc int[1];
                F(ref x, ref y);
                return x;
            });

        D1 d1 = x1 =>
            {
                Span<int> y1 = stackalloc int[1];
                F(ref x1, ref y1); // error CS8352: 'y2' may expose referenced variables
                return x1;
            };

        D2 d2 = x2 =>
            {
                Span<int> y2 = stackalloc int[1];
                F(ref x2, ref y2); // ok: F(ref object x, ref Span<int> y)
                return x2;
            };
    }
}

Untuk mengatasi perubahan resolusi kelebihan beban, gunakan jenis eksplisit untuk parameter lambda atau delegasi.

        // ok: M(D2)
        M((object x) =>
            {
                Span<int> y = stackalloc int[1];
                F(ref x, ref y); // ok: F(ref object x, ref Span<int> y)
                return x;
            });

Interpolasi string mentah pada awal baris.

Diperkenalkan di Visual Studio 2022 versi 17.5

Di .NET SDK 7.0.100 atau sebelumnya, hal berikut ini secara keliru diizinkan:

var x = $"""
    Hello
{1 + 1}
    World
    """;

Ini melanggar aturan bahwa konten baris (termasuk di mana interpolasi dimulai) harus dimulai dengan spasi putih yang sama dengan baris akhir """; . Sekarang diperlukan bahwa hal di atas ditulis sebagai:

var x = $"""
    Hello
    {1 + 1}
    World
    """;

Jenis delegasi yang disimpulkan untuk metode mencakup nilai parameter default dan params pengubah

Diperkenalkan di Visual Studio 2022 versi 17.5

Di .NET SDK 7.0.100 atau yang lebih lama, tipe delegasi yang ditebak dari metode mengabaikan nilai parameter default dan pengubah seperti params, seperti ditunjukkan dalam kode berikut:

void Method(int i = 0, params int[] xs) { }
var action = Method; // System.Action<int, int[]>
DoAction(action, 1); // ok
void DoAction(System.Action<int, int[]> a, int p) => a(p, new[] { p });

Dalam .NET SDK 7.0.200 atau yang lebih baru, metode tersebut disimpulkan sebagai jenis delegasi sintesis anonim dengan nilai dan params pengubah parameter default yang sama. Perubahan ini dapat merusak kode di atas seperti yang ditunjukkan di bawah ini:

void Method(int i = 0, params int[] xs) { }
var action = Method; // delegate void <anonymous delegate>(int arg1 = 0, params int[] arg2)
DoAction(action, 1); // error CS1503: Argument 1: cannot convert from '<anonymous delegate>' to 'System.Action<int, int[]>'
void DoAction(System.Action<int, int[]> a, int p) => a(p, new[] { p });

Anda dapat mempelajari selengkapnya tentang perubahan ini dalam proposal terkait.

Untuk tujuan analisis penetapan yang pasti, pemanggilan fungsi lokal asinkron tidak lagi diperlakukan sebagai sedang ditunggu

Diperkenalkan di Visual Studio 2022 versi 17.5

Untuk tujuan analisis penetapan yang pasti, pemanggilan fungsi lokal asinkron tidak lagi diperlakukan sebagai ditunggu dan, oleh karena itu, fungsi lokal tidak dianggap dijalankan sepenuhnya. Lihat https://github.com/dotnet/roslyn/issues/43697 untuk alasannya.

Kode di bawah ini sekarang akan melaporkan kesalahan penetapan yang pasti:

    public async Task M()
    {
        bool a;
        await M1();
        Console.WriteLine(a); // error CS0165: Use of unassigned local variable 'a'  

        async Task M1()
        {
            if ("" == String.Empty)
            {
                throw new Exception();
            }
            else
            {
                a = true;
            }
        }
    }

INoneOperation simpul untuk atribut sekarang menjadi IAttributeOperation simpul.

Diperkenalkan di Visual Studio 2022 versi 17.5, .NET SDK versi 7.0.200

Dalam versi compiler sebelumnya, IOperation pohon untuk atribut berakar dengan node INoneOperation . Kami telah menambahkan dukungan asli untuk atribut, yang berarti bahwa akar pohon sekarang adalah IAttributeOperation. Beberapa penganalisis, termasuk versi penganalisis .NET SDK yang lebih lama, tidak mengharapkan bentuk pohon ini, dan akan salah memperingatkan (atau berpotensi gagal memperingatkan) saat menemukannya. Solusi untuk ini adalah:

  • Perbarui versi penganalisis Anda, jika memungkinkan. Jika menggunakan .NET SDK atau versi lama Microsoft.CodeAnalysis.FxCopAnalyzers, perbarui ke Microsoft.CodeAnalysis.NetAnalyzers 7.0.0-preview1.22464.1 atau yang lebih baru.
  • Sembunyikan atau tahan segala deteksi positif palsu dari alat analis hingga mereka dapat diperbarui dengan versi yang mempertimbangkan perubahan ini.

Pengujian tipe untuk ref structs tidak didukung.

Diperkenalkan di Visual Studio 2022 versi 17.4

ref Ketika jenis struct digunakan dalam operator 'is' atau 'as', sebelumnya dalam beberapa skenario, compiler melaporkan peringatan yang keliru tentang pengujian jenis selalu gagal saat runtime, karena melewatkan pemeriksaan jenis yang sebenarnya, dan menyebabkan perilaku yang tidak tepat. Ketika perilaku yang salah pada waktu eksekusi dimungkinkan, pengkompilasi sekarang akan menghasilkan kesalahan sebagai gantinya.

ref struct G<T>
{
    public void Test()
    {
        if (this is G<int>) // Will now produce an error, used to be treated as always `false`.
        {

Hasil yang tidak digunakan dari ref lokal adalah proses dereferensi.

Diperkenalkan di Visual Studio 2022 versi 17.4

ref Ketika variabel lokal direferensikan berdasarkan nilai, tetapi hasilnya tidak digunakan (seperti ketika ditetapkan ke _discard_), hasilnya sebelumnya diabaikan. Kompilator sekarang akan mendereferensi variabel lokal tersebut, memastikan bahwa setiap efek samping diperhatikan.

ref int local = Unsafe.NullRef<int>();
_ = local; // Will now produce a `NullReferenceException`

Tipe tidak dapat dinamai scoped

Diperkenalkan di Visual Studio 2022 versi 17.4. Mulai dari C# 11, jenis tidak dapat diberi nama scoped. Pengkompilasi akan melaporkan kesalahan pada semua nama jenis tersebut. Untuk mengatasi hal ini, nama jenis dan semua penggunaannya harus di-escape dengan @:

class scoped {} // Error CS9056
class @scoped {} // No error
ref scoped local; // Error
ref scoped.nested local; // Error
ref @scoped local2; // No error

Ini dilakukan karena sekarang scoped adalah pengubah untuk deklarasi variabel dan dipesan sesuai ref dalam jenis ref.

Tipe tidak dapat dinamai file

Diperkenalkan di Visual Studio 2022 versi 17.4. Mulai dari C# 11, jenis tidak dapat diberi nama file. Pengkompilasi akan melaporkan kesalahan pada semua nama jenis tersebut. Untuk mengatasi hal ini, nama jenis dan semua penggunaannya harus di-escape dengan @:

class file {} // Error CS9056
class @file {} // No error

Ini dilakukan karena file sekarang menjadi pengubah dari deklarasi jenis.

Anda dapat mempelajari lebih lanjut tentang perubahan ini dalam masalah csharplang terkait.

Ruang yang diperlukan dalam arahan rentang #line

Diperkenalkan dalam .NET SDK 6.0.400, Visual Studio 2022 versi 17.3.

Ketika direktif #line rentang diperkenalkan dalam C# 10, itu tidak membutuhkan penspasian khusus.
Misalnya, ini akan valid: #line(1,2)-(3,4)5"file.cs".

Di Visual Studio 17.3, pengkompilasi memerlukan spasi sebelum tanda kurung pertama, offset karakter, dan nama file.
Jadi contoh di atas gagal diproses kecuali spasi ditambahkan: #line (1,2)-(3,4) 5 "file.cs".

Operator yang diperiksa pada System.IntPtr dan System.UIntPtr

Diperkenalkan dalam .NET SDK 7.0.100, Visual Studio 2022 versi 17.3.

Ketika platform mendukung jenis numerikIntPtr dan UIntPtr (seperti yang ditunjukkan oleh keberadaan System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr) operator bawaan dari nint dan nuint berlaku untuk jenis tersebut. Ini berarti bahwa pada platform tersebut, IntPtr dan UIntPtr memiliki operator bawaan checked , yang sekarang dapat melemparkan ketika luapan terjadi.

IntPtr M(IntPtr x, int y)
{
    checked
    {
        return x + y; // may now throw
    }
}

unsafe IntPtr M2(void* ptr)
{
    return checked((IntPtr)ptr); // may now throw
}

Kemungkinan solusinya adalah:

  1. Tentukan unchecked konteks
  2. Turunkan ke platform/TFM yang tidak memiliki jenis numerik IntPtr/UIntPtr

Selain itu, konversi implisit antara IntPtr/UIntPtr dan jenis numerik lainnya diperlakukan sebagai konversi standar pada platform tersebut. Ini dapat memengaruhi resolusi kelebihan beban dalam beberapa kasus.

Perubahan ini dapat menyebabkan perubahan perilaku jika kode pengguna bergantung pada pengecualian luapan dalam konteks yang tidak dicentang, atau jika tidak mengharapkan pengecualian luapan dalam konteks yang dicentang. Penganalisis ditambahkan dalam 7.0 untuk membantu mendeteksi perubahan perilaku tersebut dan mengambil tindakan yang sesuai. Penganalisis akan menghasilkan diagnostik pada potensi perubahan perilaku, yang secara bawaan memiliki tingkat keparahan info, namun bisa dinaikkan menjadi peringatan melalui editorconfig.

Penambahan System.UIntPtr dan System.Int32

Diperkenalkan dalam .NET SDK 7.0.100, Visual Studio 2022 versi 17.3.

Ketika platform mendukung numerikIntPtr dan UIntPtr tipe (sebagaimana ditunjukkan oleh keberadaan System.Runtime.CompilerServices.RuntimeFeature.NumericIntPtr), operator +(UIntPtr, int) yang ditentukan dalam System.UIntPtr tidak dapat lagi digunakan. Sebaliknya, menambahkan ekspresi jenis System.UIntPtr dan System.Int32 menghasilkan kesalahan:

UIntPtr M(UIntPtr x, int y)
{
    return x + y; // error: Operator '+' is ambiguous on operands of type 'nuint' and 'int'
}

Kemungkinan solusinya adalah:

  1. UIntPtr.Add(UIntPtr, int) Gunakan metode :UIntPtr.Add(x, y)
  2. Terapkan cast yang tidak dicentang untuk mengetik nuint pada operand kedua: x + unchecked((nuint)y)

Operator nameof dalam atribut metode atau fungsi lokal

Diperkenalkan dalam .NET SDK 6.0.400, Visual Studio 2022 versi 17.3.

Ketika versi bahasa adalah C# 11 atau yang lebih baru, operator nameof dalam atribut dalam metode membawa parameter jenis metode tersebut dalam ruang lingkup. Hal yang sama berlaku untuk fungsi lokal.
Operator nameof dalam atribut pada suatu metode, parameter jenisnya, atau parameternya, akan membawa parameter dari metode tersebut ke dalam cakupannya. Hal yang sama berlaku untuk fungsi lokal, lambda, delegasi, dan pengindeks.

Misalnya, ini sekarang akan menjadi kesalahan:

class C
{
  class TParameter
  {
    internal const string Constant = """";
  }
  [MyAttribute(nameof(TParameter.Constant))]
  void M<TParameter>() { }
}
class C
{
  class parameter
  {
    internal const string Constant = """";
  }
  [MyAttribute(nameof(parameter.Constant))]
  void M(int parameter) { }
}

Kemungkinan solusinya adalah:

  1. Ganti nama parameter atau parameter jenis untuk menghindari bayangan nama dari cakupan luar.
  2. Gunakan string literal, bukan operator nameof.

Tidak dapat mengirim parameter output menurut referensi

Diperkenalkan dalam .NET SDK 7.0.100, Visual Studio 2022 versi 17.3.

Dengan versi bahasa C# 11 atau yang lebih baru, atau dengan .NET 7.0 atau yang lebih baru, parameter out tidak dapat dikembalikan dengan referensi.

static ref T ReturnOutParamByRef<T>(out T t)
{
    t = default;
    return ref t; // error CS8166: Cannot return a parameter by reference 't' because it is not a ref parameter
}

Kemungkinan solusinya adalah:

  1. Gunakan System.Diagnostics.CodeAnalysis.UnscopedRefAttribute untuk menandai referensi sebagai tanpa lingkup.

    static ref T ReturnOutParamByRef<T>([UnscopedRef] out T t)
    {
        t = default;
        return ref t; // ok
    }
    
  2. Ubah tanda tangan metode untuk meneruskan parameter dengan ref.

    static ref T ReturnRefParamByRef<T>(ref T t)
    {
        t = default;
        return ref t; // ok
    }
    

Metode instans pada struktur ref dapat menangkap parameter ref yang tidak tercakup

Diperkenalkan dalam .NET SDK 7.0.100, Visual Studio 2022 versi 17.4.

Dengan versi bahasa C# 11 atau lebih baru, atau dengan .NET 7.0 atau lebih baru, pemanggilan metode instans diasumsikan untuk menangkap parameter ref struct atau ref yang tidak dibatasi ruang lingkup.

R<int> Use(R<int> r)
{
    int i = 42;
    r.MayCaptureArg(ref i); // error CS8350: may expose variables referenced by parameter 't' outside of their declaration scope
    return r;
}

ref struct R<T>
{
    public void MayCaptureArg(ref T t) { }
}

Sebuah solusi kemungkinan, jika ref atau in parameter tidak tertangkap dalam metode instans ref struct, adalah menyatakan parameter sebagai scoped ref atau scoped in.

R<int> Use(R<int> r)
{
    int i = 42;
    r.CannotCaptureArg(ref i); // ok
    return r;
}

ref struct R<T>
{
    public void CannotCaptureArg(scoped ref T t) { }
}

Metode ref struct mengembalikan analisis escape tergantung pada escape ref dari argumen ref

Diperkenalkan dalam .NET SDK 7.0.100, Visual Studio 2022 versi 17.4.

Dengan versi bahasa C# 11 atau yang lebih baru, atau dengan .NET 7.0 atau lebih baru, ref struct yang dikembalikan dari pemanggilan metode, baik sebagai nilai pengembalian atau dalam out parameter, hanya aman untuk diekspos jika semua ref dan in argumen ke pemanggilan metode adalah ref-safe-to-escape. Argumen in dapat mencakup nilai parameter default implisit.

ref struct R { }

static R MayCaptureArg(ref int i) => new R();

static R MayCaptureDefaultArg(in int i = 0) => new R();

static R Create()
{
    int i = 0;
    // error CS8347: Cannot use a result of 'MayCaptureArg(ref int)' because it may expose
    // variables referenced by parameter 'i' outside of their declaration scope
    return MayCaptureArg(ref i);
}

static R CreateDefault()
{
    // error CS8347: Cannot use a result of 'MayCaptureDefaultArg(in int)' because it may expose
    // variables referenced by parameter 'i' outside of their declaration scope
    return MayCaptureDefaultArg();
}

Solusi yang dapat digunakan, jika argumen ref atau in tidak ditangkap dalam ref struct nilai hasil, adalah mendeklarasikan parameter sebagai scoped ref atau scoped in.

static R CannotCaptureArg(scoped ref int i) => new R();

static R Create()
{
    int i = 0;
    return CannotCaptureArg(ref i); // ok
}

ref ke ref struct argumen yang dianggap tidak terlingkup dalam __arglist

Diperkenalkan dalam .NET SDK 7.0.100, Visual Studio 2022 versi 17.4.

Dengan versi bahasa C# 11 atau yang lebih baru, atau dengan .NET 7.0 atau yang lebih baru, ref ke tipe ref struct dianggap sebagai referensi yang tidak terlingkup saat diteruskan sebagai argumen ke __arglist.

ref struct R { }

class Program
{
    static void MayCaptureRef(__arglist) { }

    static void Main()
    {
        var r = new R();
        MayCaptureRef(__arglist(ref r)); // error: may expose variables outside of their declaration scope
    }
}

Operator penggeseran kanan tanpa tanda

Diperkenalkan dalam .NET SDK 6.0.400, Visual Studio 2022 versi 17.3. Bahasa menambahkan dukungan untuk operator "Unsigned Right Shift" (>>>). Ini menonaktifkan kemampuan untuk menggunakan metode yang menerapkan operator "Unsigned Right Shift" yang ditentukan pengguna sebagai metode reguler.

Misalnya, ada perpustakaan perangkat lunak yang sudah ada, dikembangkan dalam beberapa bahasa (selain VB atau C#), yang mengekspos operator "Unsigned Right Shift" yang ditentukan oleh pengguna untuk jenis C1. Kode berikut yang digunakan untuk mengkompilasi dengan sukses sebelumnya:

static C1 Test1(C1 x, int y) => C1.op_UnsignedRightShift(x, y); //error CS0571: 'C1.operator >>>(C1, int)': cannot explicitly call operator or accessor

Solusi yang mungkin adalah beralih menggunakan >>> operator:

static C1 Test1(C1 x, int y) => x >>> y;

Enumerator foreach sebagai struct referensi

Diperkenalkan dalam .NET SDK 6.0.300, Visual Studio 2022 versi 17.2. Penggunaan tipe enumerator ref struct akan melaporkan kesalahan jika versi bahasa diatur ke 7.3 atau sebelumnya.

Ini memperbaiki bug di mana fitur didukung di kompilator terbaru yang menargetkan versi C# yang lebih tua sebelum fitur tersebut benar-benar didukung.

Kemungkinan solusinya adalah:

  1. Ubah jenis ref struct menjadi jenis struct atau class.
  2. Tingkatkan elemen ke <LangVersion> 7.3 atau yang lebih baru.

Asinkron foreach lebih memilih pola berbasis DisposeAsync dibandingkan implementasi antarmuka eksplisit dari IAsyncDisposable.DisposeAsync()

Diperkenalkan dalam .NET SDK 6.0.300, Visual Studio 2022 versi 17.2. Asinkron foreach lebih suka mengikat menggunakan metode berbasis DisposeAsync() pola daripada IAsyncDisposable.DisposeAsync().

Misalnya, DisposeAsync() akan dipilih, bukan IAsyncEnumerator<int>.DisposeAsync() metode pada AsyncEnumerator:

await foreach (var i in new AsyncEnumerable())
{
}

struct AsyncEnumerable
{
    public AsyncEnumerator GetAsyncEnumerator() => new AsyncEnumerator();
}

struct AsyncEnumerator : IAsyncDisposable
{
    public int Current => 0;
    public async ValueTask<bool> MoveNextAsync()
    {
        await Task.Yield();
        return false;
    }
    public async ValueTask DisposeAsync()
    {
        Console.WriteLine("PICKED");
        await Task.Yield();
    }
    ValueTask IAsyncDisposable.DisposeAsync() => throw null; // no longer picked
}

Perubahan ini memperbaiki pelanggaran spesifikasi di mana metode publik DisposeAsync terlihat pada jenis yang dideklarasikan, sedangkan implementasi antarmuka eksplisit hanya terlihat menggunakan referensi ke jenis antarmuka.

Untuk mengatasi masalah ini, hapus metode pola berbasis DisposeAsync dari jenis Anda.

Melarang string yang dikonversi sebagai argumen default

Diperkenalkan dalam .NET SDK 6.0.300, Visual Studio 2022 versi 17.2. Pengkompilasi C# akan menerima nilai argumen default yang salah yang melibatkan konversi referensi konstanta string, dan akan memancarkan null sebagai nilai konstanta alih-alih nilai default yang ditentukan dalam sumber. Di Visual Studio 17.2, ini menjadi kesalahan. Lihat roslyn#59806.

Perubahan ini memperbaiki pelanggaran spesifikasi di pengkompilasi. Argumen default harus berupa konstanta waktu kompilasi. Versi sebelumnya mengizinkan kode berikut:

void M(IEnumerable<char> s = "hello")

Deklarasi sebelumnya memerlukan konversi dari string ke IEnumerable<char>. Pengkompilasi mengizinkan konstruksi ini, dan akan memancarkan null sebagai nilai argumen. Kode sebelumnya menghasilkan kesalahan kompilator mulai dari 17.2.

Untuk mengatasi perubahan ini, Anda bisa membuat salah satu perubahan berikut:

  1. Ubah jenis parameter sehingga konversi tidak diperlukan.
  2. Ubah nilai argumen default menjadi null untuk memulihkan perilaku sebelumnya.

Kata kunci kontekstual var sebagai tipe pengembalian eksplisit untuk lambda

Diperkenalkan di .NET SDK 6.0.200, Visual Studio 2022 versi 17.1. Var kata kunci kontekstual tidak dapat digunakan sebagai jenis pengembalian lambda eksplisit.

Perubahan ini memungkinkan fitur potensial di masa mendatang dengan memastikan bahwa var tetap merupakan jenis alami untuk jenis pengembalian ekspresi lambda.

Anda dapat mengalami kesalahan ini jika Anda memiliki jenis bernama var dan menentukan ekspresi lambda menggunakan jenis pengembalian eksplisit var.

using System;

F(var () => default);  // error CS8975: The contextual keyword 'var' cannot be used as an explicit lambda return type
F(@var () => default); // ok
F(() => default);      // ok: return type is inferred from the parameter to F()

static void F(Func<var> f) { }

public class var
{
}

Solusinya meliputi perubahan berikut:

  1. Gunakan @var sebagai jenis pengembalian.
  2. Hapus jenis pengembalian eksplisit sehingga pengkompilasi menentukan jenis pengembalian.

Pengelola string interpolasi dan inisialisasi pengindeks

Diperkenalkan di .NET SDK 6.0.200, Visual Studio 2022 versi 17.1. Pengindeks yang mengambil handler string terinterpolasi dan memerlukan penerima sebagai input untuk konstruktor tidak dapat digunakan dalam penginisialisasi objek.

Perubahan ini melarang skenario kasus tepi di mana penginisialisasi pengindeks menggunakan handler string terinterpolasi dan penghandel string terinterpolasi mengambil penerima pengindeks sebagai parameter konstruktor. Alasan perubahan ini adalah bahwa skenario ini dapat mengakibatkan akses variabel yang belum diinisialisasi. Pertimbangkan contoh ini:

using System.Runtime.CompilerServices;

// error: Interpolated string handler conversions that reference
// the instance being indexed cannot be used in indexer member initializers.
var c = new C { [$""] = 1 }; 

class C
{
    public int this[[InterpolatedStringHandlerArgument("")] CustomHandler c]
    {
        get => ...;
        set => ...;
    }
}

[InterpolatedStringHandler]
class CustomHandler
{
    // The constructor of the string handler takes a "C" instance:
    public CustomHandler(int literalLength, int formattedCount, C c) {}
}

Solusinya meliputi perubahan berikut:

  1. Hapus tipe penerima dari pengelola string terinterpolasi.
  2. Mengubah argumen ke pengindeks menjadi string

ref, readonly ref, in, out tidak diizinkan sebagai parameter atau kembali pada metode dengan penelepon yang tidak dikelola saja

Diperkenalkan di .NET SDK 6.0.200, Visual Studio 2022 versi 17.1.ref/ref readonly/in/out tidak diizinkan untuk digunakan pada pengembalian/parameter metode yang dikaitkan dengan UnmanagedCallersOnly.

Perubahan ini adalah perbaikan bug. Nilai dan parameter yang dikembalikan tidak bersifat blittable. Meneruskan argumen atau mengembalikan nilai berdasarkan referensi dapat menyebabkan perilaku yang tidak ditentukan. Tak satu pun dari deklarasi berikut akan berhasil dikompilasi:

using System.Runtime.InteropServices;
[UnmanagedCallersOnly]
static ref int M1() => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static ref readonly int M2() => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static void M3(ref int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static void M4(in int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

[UnmanagedCallersOnly]
static void M5(out int o) => throw null; // error CS8977: Cannot use 'ref', 'in', or 'out' in a method attributed with 'UnmanagedCallersOnly'.

Solusinya adalah menghapus pengubah by reference.

Panjang, Hitungan diasumsikan sebagai nilai non-negatif dalam pola

Diperkenalkan di .NET SDK 6.0.200, Visual Studio 2022 versi 17.1.Length dan Count properti pada tipe yang dapat dihitung dan dapat diindeks diasumsikan tidak negatif untuk tujuan analisis subsumsi dan pemastian kelengkapan pola dan sakelar. Jenis tersebut dapat digunakan dengan pengindeks Indeks implisit dan pola daftar.

Properti Length dan Count , meskipun ditik sebagai int, diasumsikan non-negatif saat menganalisis pola. Pertimbangkan metode sampel ini:

string SampleSizeMessage<T>(IList<T> samples)
{
    return samples switch
    {
        // This switch arm prevents a warning before 17.1, but will never happen in practice.
        // Starting with 17.1, this switch arm produces a compiler error.
        // Removing it won't introduce a warning.
        { Count: < 0 }    => throw new InvalidOperationException(),
        { Count:  0 }     => "Empty collection",
        { Count: < 5 }    => "Too small",
        { Count: < 20 }   => "reasonable for the first pass",
        { Count: < 100 }  => "reasonable",
        { Count: >= 100 } => "fine",
    };
}

void M(int[] i)
{
    if (i is { Length: -1 }) {} // error: impossible under assumption of non-negative length
}

Sebelum 17.1, perlunya pengujian terhadap Count yang negatif oleh lengan sakelar pertama adalah untuk menghindari peringatan bahwa semua nilai yang mungkin tidak tercakup. Dimulai dengan 17.1, lengan sakelar pertama menghasilkan kesalahan kompilator. Solusinya adalah menghapus lengan sakelar yang ditambahkan untuk kasus yang tidak valid.

Perubahan ini dilakukan sebagai bagian dari penambahan pola daftar. Aturan pemrosesan lebih konsisten jika setiap penggunaan Length properti atau Count pada koleksi dianggap tidak negatif. Anda dapat membaca detail selengkapnya tentang perubahan dalam masalah desain bahasa.

Cara mengatasinya adalah menghapus lengan sakelar dengan kondisi yang tidak dapat dijangkau.

Menambahkan penginisialisasi bidang ke struct memerlukan konstruktor yang dideklarasikan secara eksplisit

Diperkenalkan di .NET SDK 6.0.200, Visual Studio 2022 versi 17.1.struct deklarasi jenis dengan penginisialisasi bidang harus menyertakan konstruktor yang dideklarasikan secara eksplisit. Selain itu, semua bidang harus benar-benar ditetapkan dalam struct konstruktor instans yang tidak memiliki : this() penginisialisasi sehingga bidang yang sebelumnya tidak ditetapkan harus ditetapkan dari konstruktor yang ditambahkan atau dari inisialisasi bidang. Lihat dotnet/csharplang#5552, dotnet/roslyn#58581.

Ada dua cara untuk menginisialisasi variabel ke nilai defaultnya di C#: new() dan default. Untuk kelas, perbedaannya terbukti karena new membuat instans baru dan default mengembalikan null. Perbedaan ini lebih lembut pada struktur, karena untuk default, struktur mengembalikan sebuah instans di mana setiap bidang/properti diatur ke defaultnya masing-masing. Kami menambahkan inisialisasi bidang untuk struktur di C# 10. Penginisialisasi variabel dijalankan hanya ketika konstruktor yang dinyatakan secara eksplisit dijalankan. Pada dasarnya, mereka tidak berfungsi saat Anda menggunakan default atau membuat array dari jenis struct apa pun.

Dalam versi 17.0, jika terdapat penginisialisasi bidang tetapi tidak ada konstruktor yang dideklarasikan, maka sebuah konstruktor tanpa parameter dihasilkan yang akan menjalankan penginisialisasi bidang tersebut. Namun, yang berarti menambahkan atau menghapus deklarasi konstruktor dapat memengaruhi apakah konstruktor tanpa parameter disintesis, dan sebagai hasilnya, dapat mengubah perilaku new().

Untuk mengatasi masalah ini, di .NET SDK 6.0.200 (VS 17.1) kompilator tidak lagi mensintesis konstruktor tanpa parameter. struct Jika berisi penginisialisasi bidang dan tidak ada konstruktor eksplisit, kompilator menghasilkan kesalahan. Jika struct memiliki penginisialisasi bidang, itu harus mendeklarasikan konstruktor, karena jika tidak, penginisialisasi bidang tidak akan pernah dijalankan.

Selain itu, semua atribut yang tidak memiliki penginisialisasi atribut harus ditetapkan di setiap struct konstruktor kecuali konstruktor memiliki : this() penginisialisasi.

Misalnya:

struct S // error CS8983: A 'struct' with field initializers must include an explicitly declared constructor.
{
    int X = 1; 
    int Y;
}

Solusinya adalah mendeklarasikan konstruktor. Kecuali bidang sebelumnya tidak ditetapkan, konstruktor ini dapat, dan sering kali akan, menjadi konstruktor tanpa parameter kosong:

struct S
{
    int X = 1;
    int Y;

    public S() { Y = 0; } // ok
}

Penentu format tidak boleh berisi kurung kurawal

Diperkenalkan di .NET SDK 6.0.200, Visual Studio 2022 versi 17.1. Penentu format dalam string terinterpolasi tidak boleh berisi kurung kurawal (baik { atau }). Dalam versi {{ sebelumnya ditafsirkan sebagai escaped { dan }} ditafsirkan sebagai karakter escaped } dalam penentu format. Sekarang karakter pertama } dalam penentu format mengakhiri interpolasi, dan karakter apa pun { adalah kesalahan.

Hal ini membuat pemrosesan string terinterpolasi konsisten dengan pemrosesan untuk System.String.Format:

using System;
Console.WriteLine($"{{{12:X}}}");
//prints now: "{C}" - not "{X}}"

X adalah format untuk heksadesimal huruf besar dan C merupakan nilai heksadesimal untuk 12.

Solusinya adalah menghapus kurung kurawal tambahan dalam string format.

Anda dapat mempelajari selengkapnya tentang perubahan ini dalam masalah roslyn terkait.

Tipe tidak dapat dinamai required

Diperkenalkan di Visual Studio 2022 versi 17.3. Mulai dari C# 11, jenis tidak dapat diberi nama required. Pengkompilasi akan melaporkan kesalahan pada semua nama jenis tersebut. Untuk mengatasi hal ini, nama jenis dan semua penggunaannya harus di-escape dengan @:

class required {} // Error CS9029
class @required {} // No error

Ini dilakukan karena required kini menjadi pengubah anggota untuk properti dan kolom.

Anda dapat mempelajari lebih lanjut tentang perubahan ini dalam masalah csharplang terkait.