Cicli di avviso C#

In ogni versione del compilatore C# possono essere introdotti nuovi avvisi ed errori. Quando è possibile segnalare nuovi avvisi sul codice esistente, tali avvisi vengono introdotti in un sistema di consenso esplicito denominato ciclo di avviso. Il sistema di consenso esplicito indica che non dovrebbero essere visualizzati nuovi avvisi sul codice esistente senza intervenire per abilitarli. I cicli di avviso vengono abilitati usando l'elemento AnalysisLevel nel file di progetto. Quando si specifica <TreatWarningsAsErrors>true</TreatWarningsAsErrors>, gli avvisi del ciclo di avviso abilitati generano errori. La diagnostica del ciclo di avviso 5 è stata aggiunta in C# 9. La diagnostica del ciclo di avviso 6 è stata aggiunta in C# 10. La diagnostica del ciclo di avviso 7 è stata aggiunta in C# 11. La diagnostica del ciclo di avviso 8 è stata aggiunta in C# 12.

CS9123: l'acquisizione dell'indirizzo del parametro o della variabile locale nel metodo asincrono può creare una perdita di memoria di Garbage Collection.

Ciclo di avviso 8

L'operatore & non deve essere usato nei parametri o nelle variabili locali nei metodi asincroni. Il codice seguente genera l'errore CS9123:

public static async Task LogValue()
{
    int x = 1;
    unsafe {
        int* y = &x;
        Console.WriteLine(*y);
    }
    await Task.Delay(1000);
}

CS8981: il nome del tipo contiene solo caratteri ASCII minuscoli.

Ciclo di avviso 7

Tutte le nuove parole chiave aggiunte per C# saranno espresse in caratteri ASCII minuscoli. Questo avviso garantisce che nessuno dei tipi sia in conflitto con le parole chiave future. Il codice seguente genera l'errore CS8981:

public class lowercasename
{
}

È possibile risolvere questo avviso rinominando il tipo in modo da includere almeno un carattere ASCII non minuscolo, ad esempio un carattere maiuscolo, un numero o un carattere di sottolineatura.

CS8826: dichiarazioni di metodo parziali presentano differenze di firma.

Ciclo di avviso 6

Questo avviso corregge alcune incoerenze nella segnalazione di differenze tra firme di metodi parziali. Il compilatore ha sempre segnalato un errore quando le firme del metodo parziale creano firme CLR diverse. Il compilatore segnala ora l'errore CS8826 quando le firme sono sintatticamente diverse da C#. Si consideri la classe parziale seguente:

public partial class PartialType
{
    public partial void M1(int x);

    public partial T M2<T>(string s) where T : struct;

    public partial void M3(string s);


    public partial void M4(object o);
    public partial void M5(dynamic o);
    public partial void M6(string? s);
}

L'implementazione della classe parziale seguente genera diversi esempi di errori: CS8626:

public partial class PartialType
{
    // Different parameter names:
    public partial void M1(int y) { }

    // Different type parameter names:
    public partial TResult M2<TResult>(string s) where TResult : struct => default;

    // Relaxed nullability
    public partial void M3(string? s) { }


    // Mixing object and dynamic
    public partial void M4(dynamic o) { }

    // Mixing object and dynamic
    public partial void M5(object o) { }

    // Note: This generates CS8611 (nullability mismatch) not CS8826
    public partial void M6(string s) { }
}

Nota

Se l'implementazione di un metodo usa un tipo riferimento non nullable quando l'altra dichiarazione accetta tipi riferimento che non ammette i valori Null, viene generato l'errore CS8611 anziché CS8826.

Per correggere qualsiasi istanza di questi avvisi, verificare che le due firme corrispondano.

CS7023: un tipo statico viene usato in un'espressione 'is' o 'as'.

Ciclo di avviso 5

Le espressioni is e as restituiscono sempre false per un tipo statico perché non è possibile creare istanze di un tipo statico. Il codice seguente genera l'errore CS7023:

static class StaticClass
{
    public static void Thing() { }
}

void M(object o)
{
    // warning: cannot use a static type in 'is' or 'as'
    if (o is StaticClass)
    {
        Console.WriteLine("Can't happen");
    }
    else
    {
        Console.WriteLine("o is not an instance of a static class");
    }
}

Il compilatore segnala questo avviso perché il test del tipo non può mai avere esito positivo. Per correggere questo avviso, rimuovere il test e rimuovere il codice eseguito solo se il test è riuscito. Nell'esempio precedente viene sempre eseguita la clausola else. Il corpo del metodo può essere sostituito da questa singola riga:

Console.WriteLine("o is not an instance of a static class");

CS8073 : il risultato dell'espressione è sempre 'false' (o 'true').

Ciclo di avviso 5

Gli operatori == e != restituiscono sempre false (o true) quando si confronta un'istanza di un tipo struct con null. Questo avviso è illustrato nel codice riportato di seguito. Si supponga che S sia uno struct in cui sono definiti operator == e operator !=:

class Program
{
    public static void M(S s)
    {
        if (s == null) { } // CS8073: The result of the expression is always 'false'
        if (s != null) { } // CS8073: The result of the expression is always 'true'
    }
}

struct S
{
    public static bool operator ==(S s1, S s2) => s1.Equals(s2);
    public static bool operator !=(S s1, S s2) => !s1.Equals(s2);
    public override bool Equals(object? other)
    {
        // Implementation elided
        return false;
    }
    public override int GetHashCode() => 0;

    // Other details elided...
}

Per correggere questo errore, rimuovere il controllo Null e il codice che verrebbero eseguiti se l'oggetto è null.

CS8848 : l'operatore 'from' non può essere usato qui a causa della precedenza. Usare le parentesi per evitare ambiguità.

Ciclo di avviso 5

Negli esempi seguenti viene illustrato questo avviso. L'espressione viene associata in modo non corretto a causa della precedenza degli operatori.

bool b = true;
var source = new Src();
b = true;
source = new Src();
var a = b && from c in source select c;
Console.WriteLine(a);

var indexes = new Src2();
int[] array = { 1, 2, 3, 4, 5, 6, 7 };
var range = array[0..from c in indexes select c];

Per correggere questo errore, racchiudere l'espressione di query tra parentesi:

bool b = true;
var source = new Src();
b = true;
source = new Src();
var a = b && (from c in source select c);
Console.WriteLine(a);

var indexes = new Src2();
int[] array = { 1, 2, 3, 4, 5, 6, 7 };
var range = array[0..(from c in indexes select c)];

Assegnazione completa ai membri, uso di una variabile non assegnata (CS8880, CS8881, CS8882, CS8883, CS8884, CS8885, CS8886, CS8887)

Ciclo di avviso 5

Diversi avvisi migliorano l'analisi dell'assegnazione definita per i tipi struct dichiarati negli assembly importati. Tutti questi nuovi avvisi vengono generati quando uno struct in un assembly importato include un campo inaccessibile (in genere un campo private) di un tipo di riferimento, come illustrato nell'esempio seguente:

public struct Struct
{
    private string data = String.Empty;
    public Struct() { }
}

Gli esempi seguenti mostrano gli avvisi generati dall'analisi dell'assegnazione definita migliorata:

  • CS8880: è necessario assegnare completamente una proprietà 'Property' implementata automaticamente prima che il controllo venga restituito al chiamante.
  • CS8881: il campo 'field' deve essere assegnato completamente prima che il controllo venga restituito al chiamante.
  • CS8882: il parametro di output 'parameter' deve essere assegnato prima che il controllo lasci il metodo corrente.
  • CS8883: uso della proprietà 'Property' implementata automaticamente probabilmente non assegnata.
  • CS8884: uso del campo 'field' probabilmente non assegnato
  • CS8885: non è possibile usare l'oggetto 'this' prima dell'assegnazione di tutti i relativi campi.
  • CS8886: uso del parametro di output 'parameterName' non assegnato.
  • CS8887: uso della variabile locale 'variableName' non assegnata
public struct DefiniteAssignmentWarnings
{
    // CS8880
    public Struct Property { get; }
    // CS8881
    private Struct field;

    // CS8882
    public void Method(out Struct s)
    {

    }

    public DefiniteAssignmentWarnings(int dummy)
    {
        // CS8883
        Struct v2 = Property;
        // CS8884
        Struct v3 = field;
        // CS8885:
        DefiniteAssignmentWarnings p2 = this;
    }

    public static void Method2(out Struct s1)
    {
        // CS8886
        var s2 = s1;
        s1 = default;
    }

    public static void UseLocalStruct()
    {
        Struct r1;
        var r2 = r1;
    }
}

È possibile correggere questi avvisi inizializzando o assegnando lo struct importato al valore predefinito:

public struct DefiniteAssignmentNoWarnings
{
    // CS8880
    public Struct Property { get; } = default;
    // CS8881
    private Struct field = default;

    // CS8882
    public void Method(out Struct s)
    {
        s = default;
    }

    public DefiniteAssignmentNoWarnings(int dummy)
    {
        // CS8883
        Struct v2 = Property;
        // CS8884
        Struct v3 = field;
        // CS8885:
        DefiniteAssignmentNoWarnings p2 = this;
    }

    public static void Method2(out Struct s1)
    {
        // CS8886
        s1 = default;
        var s2 = s1;
    }

    public static void UseLocalStruct()
    {
        Struct r1 = default;
        var r2 = r1;
    }
}

CS8892: il metodo non verrà usato come punto di ingresso perché è stato trovato un punto di ingresso sincrono 'method'.

Ciclo di avviso 5

Questo avviso viene generato in tutti i candidati come punto di ingresso asincrono quando sono presenti più punti di ingresso validi, incluso uno o più punti di ingresso sincroni.

L'esempio seguente genera l'avviso CS8892:

public static void Main()
{
    RunProgram();
}

// CS8892
public static async Task Main(string[] args)
{
    await RunProgramAsync();
}

Nota

Il compilatore usa sempre il punto di ingresso sincrono. Se sono presenti più punti di ingresso sincroni, viene visualizzato un errore del compilatore.

Per correggere questo avviso, rimuovere o rinominare il punto di ingresso asincrono.

CS8897: i tipi statici non possono essere usati come parametri

Ciclo di avviso 5

I membri di un'interfaccia non possono dichiarare parametri il cui tipo è una classe statica. Il codice seguente illustra gli CS8897 e CS8898:

public static class Utilities
{
    // elided
}

public interface IUtility
{
    // CS8897
    public void SetUtility(Utilities u);

    // CS8898
    public Utilities GetUtility();
}

Per correggere questo avviso, modificare il tipo di parametro o rimuovere il metodo.

CS8898: i tipi statici non possono essere utilizzati come tipi restituiti

Ciclo di avviso 5

I membri di un'interfaccia non possono dichiarare un tipo restituito che è una classe statica. Il codice seguente illustra gli CS8897 e CS8898:

public static class Utilities
{
    // elided
}

public interface IUtility
{
    // CS8897
    public void SetUtility(Utilities u);

    // CS8898
    public Utilities GetUtility();
}

Per correggere questo avviso, modificare il tipo restituito o rimuovere il metodo.