Esiste un "opposto" all'operatore di coalescenza nullo? (... in qualsiasi lingua?)


92

null coalescing si traduce approssimativamente in return x, unless it is null, in which case return y

Ho spesso bisogno return null if x is null, otherwise return x.y

posso usare return x == null ? null : x.y;

Non male, ma quello nullnel mezzo mi dà sempre fastidio - sembra superfluo. Preferirei qualcosa di simile return x :: x.y;, dove ciò che segue ::viene valutato solo se ciò che lo precede non lo è null.

Lo vedo quasi l'opposto della coalescenza nulla, un po 'mescolato con un controllo null inline conciso, ma sono [ quasi ] certo che non esiste un tale operatore in C #.

Esistono altre lingue che hanno un tale operatore? se è così, come è chiamato?

(So ​​che posso scrivere un metodo per questo in C #; lo uso return NullOrValue.of(x, () => x.y);, ma se hai qualcosa di meglio, mi piacerebbe vedere anche quello.)


Alcuni hanno chiesto qualcosa come x? .Y in C #, ma non esiste niente del genere.
Anthony Pegram

1
@ Anthony Oh, sarebbe bellissimo. Grazie.
Jay

2
In c ++, sarebbe abbastanza facile da esprimere come return x ? x.y : NULL. Yay per convertire i tipi di puntatore in booleani!
Phil Miller

1
@Novelocrat questa è una delle cose che mi irrita di più in C # è che non hanno seguito la C se questo se (qualcosa) = vero tranne quando è se (0, falso, nullo)
Chris Marisic

2
@ Chris: questa non è un'affermazione accurata su C. Se hai una variabile non scalare (come una struttura), non puoi usarla in una condizione.
Phil Miller

Risposte:


62

C'è l' operatore di dereferenziazione null-safe (?.) In Groovy ... Penso che sia quello che stai cercando.

(È anche chiamato operatore di navigazione sicura .)

Per esempio:

homePostcode = person?.homeAddress?.postcode

Questo darà null se person, person.homeAddresso person.homeAddress.postcodeè null.

(Questo è ora disponibile in C # 6.0 ma non nelle versioni precedenti)


1
Groovy ha anche l '"operatore Elvis", che consente valori predefiniti diversi da null, ad esempio:def bar = foo ?: "<null>"
Craig Stuntz

28
Nomino questa funzionalità per C # 5.0. Non so né mi interessa cosa sia effettivamente Groovy, ma è abbastanza intuitivo da poter essere utilizzato in qualsiasi lingua.
György Andrasek

Tuttavia è stato aggiunto a C # 6 ora, evviva.
Jeff Mercado

1
Lavori di sicurezza di navigazione per l'esempio banale pubblicato, ma è ancora bisogno di usare l'operatore ternario se avete intenzione di utilizzare il valore non nullo in una chiamata di funzione, per esempio: Decimal? post = pre == null ? null : SomeMethod( pre );. Sarebbe bello se potessi ridurlo a "Decimal? Post = pre :: SomeMethod (pre);"
Dai

@Dai: dato il numero di permutazioni che potresti desiderare, sono abbastanza contento di quello che abbiamo.
Jon Skeet

36

Abbiamo considerato l'aggiunta di?. a C # 4. Non ha fatto il taglio; è una funzione "carina da avere", non una funzione "da avere". Lo prenderemo in considerazione di nuovo per ipotetiche versioni future del linguaggio, ma non terrei il respiro in attesa se fossi in te. Non è probabile che diventi più cruciale col passare del tempo. :-)


La facilità di implementazione di una funzionalità influisce sulla decisione di aggiungerla? Lo chiedo perché l'implementazione di quell'operatore sembra un'aggiunta abbastanza diretta e semplice al linguaggio. Non sono un esperto (solo un neolaureato con un grande amore per C #), quindi per favore correggimi!
Nick Strupat

14
@nick: certo, l'implementazione del lexer e del parser richiede cinque minuti di lavoro. Quindi inizi a preoccuparti di cose come "il motore IntelliSense lo gestisce bene?" e "come si comporta il sistema di ripristino degli errori quando hai digitato? ma non ancora?" e quante cose diverse fa "." intendo comunque in C #, e quanti di loro meritano di avere un? prima di esso, e quali dovrebbero essere i messaggi di errore se lo sbagli, e quanto test sarà necessario per questa funzione (suggerimento: molto.) E come lo documenteremo e comunicheremo il cambiamento, e ...
Eric Lippert

3
@nick: per rispondere alla tua domanda - sì, il costo di implementazione è un fattore, ma piccolo. Non ci sono funzionalità economiche al livello in cui lavoriamo, solo funzionalità più o meno costose. Cinque dollari di lavoro di sviluppo per far funzionare il parser nel caso del codice corretto possono facilmente trasformarsi in decine di migliaia di dollari di progettazione, implementazione, test, documentazione e istruzione.
Eric Lippert

11
@EricLippert Penso che questo sarebbe uno dei PRINCIPALI "piacevoli da avere", che hanno reso C # un tale successo e inoltre servirebbero perfettamente alla missione di rendere il codice il più conciso ed espressivo possibile!
Konstantin

Mi piacerebbe vedere questo aggiunto, quello che voglio è l'operatore elvis: stackoverflow.com/questions/2929836/...
Chris Marisic

16

Se hai un tipo speciale di logica booleana di cortocircuito, puoi farlo (esempio javascript):

return x && x.y;

Se xè nullo, non verrà valutato x.y.


2
Tranne questo anche cortocircuiti su 0, "" e NaN, quindi non è l'opposto di ??. È l'opposto di ||.
ritaj

7

Mi è sembrato giusto aggiungere questo come risposta.

Immagino che il motivo per cui non esiste nulla di simile in C # è perché, a differenza dell'operatore di coalescenza (che è valido solo per i tipi di riferimento), l'operazione inversa potrebbe produrre un tipo di riferimento o di valore (cioè class xcon membro int y- quindi sarebbe sfortunatamente inutilizzabile in molte situazioni.

Non dico però che non mi piacerebbe vederlo!

Una potenziale soluzione a questo problema sarebbe che l'operatore elevasse automaticamente un'espressione del tipo di valore sul lato destro a un nullable. Ma poi hai il problema che x.ydove y è un int restituirà effettivamente un int?che sarebbe un dolore.

Un'altra soluzione, probabilmente migliore, sarebbe che l'operatore restituisse il valore predefinito (cioè null o zero) per il tipo sul lato destro se l'espressione a sinistra è null. Ma poi hai problemi a distinguere gli scenari in cui uno zero / null è stato effettivamente letto x.yo se è stato fornito dall'operatore di accesso sicuro.


quando ho letto per la prima volta la domanda dell'OP, questo esatto problema mi è venuto in mente, ma non sono riuscito a capire come articolarlo. È un problema molto serio. +1
rmeador

non è un problema serio. Basta usarlo int?come valore di ritorno predefinito e l'utente può cambiarlo in un int se lo desidera. Ad esempio, se desidero chiamare un metodo Lookupcon un valore predefinito di -1 nel caso in cui uno dei miei riferimenti sia null:foo?.Bar?.Lookup(baz) ?? -1
Qwertie

Che ne dici di avere ?. : essere un operatore ternario, dove il lato destro deve essere dello stesso tipo del membro appropriato dato nel mezzo?
supercat

6

Delphi ha l'operatore: (invece di.), Che è null-safe.

Stavano pensando di aggiungere un?. operatore in C # 4.0 per fare lo stesso, ma questo ha ottenuto il blocco.

Nel frattempo, c'è IfNotNull () che tipo di graffi che prude. È certamente più grande di?. oppure:, ma ti consente di comporre una catena di operazioni che non genereranno un'eccezione NullReferenceException se uno dei membri è nullo.


Puoi fare un esempio di utilizzo di questo operatore per favore?
Max Carroll

1
Il ?.è una caratteristica del linguaggio C # 6.0, e sì, fa esattamente quello che l'OP ha chiesto qui. Meglio tardi che mai ^^
T_D

3

In Haskell, puoi usare il >> operatore:

  • Nothing >> Nothing è Nothing
  • Nothing >> Just 1 è Nothing
  • Just 2 >> Nothing è Nothing
  • Just 2 >> Just 1 è Just 1

3

Haskell ha fmap, che in questo caso penso sia equivalente a Data.Maybe.map. Haskell è puramente funzionale, quindi quello che stai cercando sarebbe

fmap select_y x

Se xè Nothing, questo ritorna Nothing. Se xè Just object, questo ritorna Just (select_y object). Non bella come la notazione a punti, ma dato che è un linguaggio funzionale, gli stili sono diversi.


2

PowerShell consente di fare riferimento alle proprietà (ma non ai metodi di chiamata) su un riferimento null e restituirà null se l'istanza è null. Puoi farlo a qualsiasi profondità. Avevo sperato che la funzionalità dinamica di C # 4 lo supportasse, ma non è così.

$x = $null
$result = $x.y  # $result is null

$x = New-Object PSObject
$x | Add-Member NoteProperty y 'test'
$result = $x.y  # $result is 'test'

Non è carino ma potresti aggiungere un metodo di estensione che funzionerà nel modo in cui descrivi.

public static TResult SafeGet<T, TResult>(this T obj, Func<T, TResult> selector) {
    if (obj == null) { return default(TResult); }
    else { return selector(obj); }
}

var myClass = new MyClass();
var result = myClass.SafeGet(x=>x.SomeProp);

2
public class ok<T> {
    T s;
    public static implicit operator ok<T>(T s) { return new ok<T> { s = s }; }
    public static implicit operator T(ok<T> _) { return _.s; }

    public static bool operator true(ok<T> _) { return _.s != null; }
    public static bool operator false(ok<T> _) { return _.s == null; }
    public static ok<T> operator &(ok<T> x, ok<T> y) { return y; }
}

Spesso ho bisogno di questa logica per le stringhe:

using ok = ok<string>;

...

string bob = null;
string joe = "joe";

string name = (ok)bob && bob.ToUpper();   // name == null, no error thrown
string boss = (ok)joe && joe.ToUpper();   // boss == "JOE"

1

Crea un'istanza statica della tua classe da qualche parte con tutti i valori predefiniti corretti per i membri.

Per esempio:

z = new Thingy { y=null };

quindi invece del tuo

return x != null ? x.y : null;

tu puoi scrivere

return (x ?? z).y;

A volte uso quella tecnica (ad es. (X ?? "") .ToString ()) ma è pratica solo su alcuni tipi di dati come POD e stringhe.
Qwertie


1

Il cosiddetto "operatore condizionale null" è stato introdotto in C # 6.0 e in Visual Basic 14.
In molte situazioni può essere utilizzato come l'esatto opposto dell'operatore di coalescenza null:

int? length = customers?.Length; // null if customers is null   
Customer first = customers?[0];  // null if customers is null  
int? count = customers?[0]?.Orders?.Count();  // null if customers, the first customer, or Orders is null

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/null-conditional-operators

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.