Quale è ?[]? sintassi in C #?


85

Mentre studiavo il delegato che in realtà è una classe astratta Delegate.cs, ho visto il seguente metodo in cui non capisco

  • Perché il valore restituito utilizza ?sebbene sia già un tipo di riferimento ( classe )
  • ?[]? significato sul parametro

Potresti spiegare?

public static Delegate? Combine(params Delegate?[]? delegates)
{
    if (delegates == null || delegates.Length == 0)
        return null;

    Delegate? d = delegates[0];
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}


23
Non è un array nullable che può contenere valori nullable?
Cid

3
c # 8, è ora possibile specificare che una variabile oggetto non può essere nulla. Se si flip che flag di compilazione, è necessario specificare tutte le variabili che è permesso di essere nullo.
Jeremy Lakeman,

Risposte:


66

Spiegazione dettagliata:

params Delegate?[] delegates - È una matrice di nullable Delegate

params Delegate?[]? delegates - L'intero array può essere nullable

Poiché ogni parametro è del tipo Delegate?e si restituisce un indice Delegate?[]?dell'array, è logico che il tipo restituito sia Delegate?altrimenti il ​​compilatore restituirebbe un errore come se si stesse effettuando la ritrasmissione e intda un metodo che restituisce una stringa.

Ad esempio, è possibile modificare il codice per restituire un Delegatetipo come questo:

public static Delegate Combine(params Delegate?[]? delegates)
{
    Delegate defaulDelegate = // someDelegate here
    if (delegates == null || delegates.Length == 0)
        return defaulDelegate;

    Delegate d = delegates[0] ?? defaulDelegate;
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}

4
che mi dici della mia prima domanda? Perché il valore restituito viene utilizzato ?anche se è già di tipo di riferimento (classe)
snr - Ripristina Monica il

2
Perché stai tornando d qual è il delegato? genere. E anche null o delegato a seconda dei parametri
Athanasios Kataras

2
@snr Il valore restituito utilizza ?esattamente lo stesso motivo per cui il valore argomento utilizza il valore ?. Se capisci il secondo, capisci automaticamente il primo. Perché il metodo può restituire null , come il parametro può accettare null . Cioè, entrambi possono contenere null .
GSerg

2
L'ho toccato. Ho appena risposto come nella seconda parte con l'esempio. Since each parameter is of the type Delegate? and you return an index of the Delegate?[]? array, then it makes sense that the return type is Delegate?
Athanasios Kataras,

2
@snr: Perché il valore restituito utilizza? sebbene sia già di tipo di riferimento (classe) (1) Potrebbe essere un'abitudine o un residuo di refactoring se le strutture sono usate anche in codice simile (2) In C # 8 i tipi di riferimento possono essere vietati per essere intrinsecamente nullable (quindi richiedere esplicitamente nullabilità ( 3) Foo?ha una HasValueproprietà ordinata , mentre Foodeve essere == nullverificato (4) Comunica agli sviluppatori che leggono la firma del metodo che i null sono un risultato possibile, prevedibile e corretto. (5) Il codice sembra essere scritto da qualcuno che è molto appassionato di nullabilità e di essere esplicito al riguardo.
Flater,

25

I tipi di riferimento Nullable sono nuovi in ​​C # 8.0, non esistevano prima.

È una questione di documentazione e di come vengono prodotti gli avvisi in fase di compilazione.

L'eccezione "oggetto non impostato su un'istanza di un oggetto" è un'eccezione comune. Ma questa è un'eccezione di runtime, che può essere parzialmente rilevata al momento della compilazione.

Per un regolamento Delegate dpuoi sempre chiamare

 d.Invoke();

vale a dire, puoi codificarlo, al momento della compilazione non accadrà nulla. Potrebbe sollevare eccezioni in fase di esecuzione.

Mentre per un nuovo Delegate? pquesto codice

 p.Invoke();

produrrà un avviso del compilatore. CS8602: Dereference of a possibly null reference a meno che tu non scriva:

 p?.Invoke();

cosa significa, chiamare solo se non nullo.

Quindi documentate che una variabile può contenere null o no. Genera avvisi in precedenza e può evitare più test per null. Lo stesso che hai per int e int ?. Sai per certo, l'uno non è nullo e sai come convertirlo l'uno nell'altro.


con l'avvertenza che non conosci per certo che Delegatenon è nulla. Fai semplicemente finta di sapere per certo (il che è abbastanza buono nella maggior parte dei casi).
Tim Pohlmann,

@TimPohlmann Oltre a int i = null è impossibile, anche una stringa s = null è impossibile (in C # 8 con questo cambiamento di rottura). Quindi è poco più che fingere qualcosa. Per compatibilità con le versioni precedenti è stato ridotto a un avviso. Se aggiorni l'avviso a un errore, sai per certo che non è nullo.
Holger,

4
L'ultima volta che ho verificato i NRT non sono a prova di proiettile al 100%. Esistono alcuni casi limite che il compilatore non è in grado di rilevare.
Tim Pohlmann,

Sì, ma è lo stesso degli avvisi "variabile non inizializzata" o "non tutti i percorsi di codice che restituiscono un valore". Sono tutte funzionalità al 100% in fase di compilazione, senza rilevamento in fase di esecuzione. A differenza di int ?, penso che non aggiungano memoria aggiuntiva per contenere informazioni aggiuntive. Quindi diciamo che è affidabile come intellisense. Piuttosto buono.
Holger,

1
Se ne hai uno, intnon potrà mai essere nullo. Se ne hai uno Delegate, può essere nullo (per vari motivi, ad esempio la riflessione). Di solito è sicuro supporre che Delegate(in C # 8 con NRT abilitato) non sia nullo, ma non si sa mai con certezza (dove intlo sappiamo per certo).
Tim Pohlmann,

5

In C # 8 si dovrebbero esplicitamente contrassegnare i tipi di riferimento come nullable.

Per impostazione predefinita, questi tipi non sono in grado di contenere null, un po 'simili ai tipi di valore. Anche se questo non cambia il modo in cui le cose funzionano sotto il cofano, il controllo del tipo ti richiederà di farlo manualmente.

Il codice specificato viene refactored per funzionare con C # 8, ma non beneficia di questa nuova funzionalità.

public static Delegate? Combine(params Delegate?[]? delegates)
{
    // ...[]? delegates - is not null-safe, so check for null and emptiness
    if (delegates == null || delegates.Length == 0)
        return null;

    // Delegate? d - is not null-safe too
    Delegate? d = delegates[0];
    for (int i = 1; i < delegates.Length; i++)
        d = Combine(d, delegates[i]);

    return d;
}

Ecco un esempio di un codice aggiornato (non funzionante, solo un'idea) che sfrutta questa funzionalità. Ci ha salvato da un controllo nullo e ha semplificato un po 'questo metodo.

public static Delegate? Combine(params Delegate[] delegates)
{
    // `...[] delegates` - is null-safe, so just check if array is empty
    if (delegates.Length == 0) return null;

    // `d` - is null-safe too, since we know for sure `delegates` is both not null and not empty
    Delegate d = delegates[0];

    for (int i = 1; i < delegates.Length; i++)
        // then here is a problem if `Combine` returns nullable
        // probably, we can add some null-checks here OR mark `d` as nullable
        d = Combine(d, delegates[i]);

    return d;
}

2
those types are not able to contain null, nota che genera un avviso del compilatore , non un errore. Il codice funzionerà correttamente (anche se potrebbe generare NullReferenceExceptions). Questo viene fatto tenendo presente la compatibilità con le versioni precedenti.
JAD
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.