2010-11-05 12 views
6

Eventuali duplicati:
Shortcut for “null if object is null, or object.member if object is not null”Operatore di navigazione sicura in C#?

Alcune lingue hanno un operatore di navigazione sicura che permette di realizzare non ti preoccupi di eccezioni riferimento null.

Esempio della lingua Groovy:

String lname = person.Name.ToLowerCase(); //throws exception if Name is null 
String lname = person.Name?.ToLowerCase();//lname will be null if Name was null 

Come posso realizzare qualcosa di simile a questo in C#? La mia soluzione finora è un metodo di estensione come questo:

public static T o<T>(this T obj) where T : new() 
{ 
      return obj != null ? obj : new T(); 
} 
//used like: String lname = person.o().Name; //returns null if person was null 

Tuttavia, questo funziona solo in alcuni casi.

+0

http://visualstudio.uservoice.com/forums/121579-visual-studio/suggerimenti/3990187-add-operator-to-c-? Tracking_code = 594c10a522f8e9bc987ee4a5e2c0b38d – Hawxby

+0

In questo momento di scrittura, tale funzione è effettivamente prevista per C# 6: vedere [.NET Compiler Platform ("Roslyn"): Stato della funzione lingua: Operatore di propagazione nullo '? .'] (http://roslyn.codeplex.com/discussions/540883) – stakx

risposta

9

Per tali casi Io tendo ad usare un metodo di estensione chiamato IfNotNull:

public static OUT IfNotNull<IN, OUT>(this IN v, Func<IN, OUT> f) 
    where IN : class where OUT : class 
{ 
    return v == null ? null : f(v); 
} 

Più sofisticato è introdurre la nozione di un Forse. Un esempio è stato proposto da derick bailey here.

Aggiornamento:

Dal C# 6 ora c'è un operatore di null-propagazione che la sintassi-saggio appare esattamente come quello Groovy.

+1

Questa sembra una soluzione piuttosto buona. Consente di non interrompere il concatenamento dei metodi e di non dover introdurre una variabile temporanea, che sono i motivi principali per cui voglio utilizzare un operatore di navigazione sicuro. – Kyle

+1

Dipende dal fatto che 'OUT' è un tipo nullable. Puoi estenderlo a tutti i tipi: 1. rimuovi 'where OUT: class', 2. altera il corpo a' return v == null? default (OUT): f (v); '. –

+0

In che modo v deriva da o? Questo è il punto, essere in grado di fare sth con o se non è nullo. – flq

3

Non so di tornare un null da qualcosa che è garantito per non essere nulla, ma per garantire un riferimento a un oggetto, è possibile utilizzare la Qualcosa Null Coalescing Operator??

come:

string lname = (person.Name??String.Empty).ToLower(); 

E ' restituirà una stringa vuota anziché null per il caso nullo, ma funzionerà.

Il ritorno di una stringa vuota ha più senso che restituire un valore null; se si restituisce un valore nullo, esso verrà gettato di nuovo se si concentra un altro operatore su di esso.

+0

È possibile scrivere un metodo di estensione' ToLowerCase', ma probabilmente utilizzerei solo il built-in nel metodo 'ToLower' sull'oggetto stringa. –

+0

+1 per usare l'operatore '??'. – ThiefMaster

+1

?? è il mio operatore C# preferito. – Bryan

2

Attualmente non esiste in C#, ma è possibile scriverlo con SelectMany.

String lname = from _ in person.Name from s in _.ToUpper() select s; 

o

String lname = person.Name.SelectMany(_ => _.ToUpper(), s => s); 

(Quella era la proposta di Bart De Smet in his PDC 2010 talk on the future of LINQ Vedi slitta # 6..)

+0

Pretty opaque .. –

+1

Nota che 'SelectMany' in questo caso non è il solito LINQ' SelectMany' ma un 'SelectMany' personalizzato creato per implementare la monade' Maybe'. –

4

Si sta cercando il cortocircuito null-conditional member access operator ?. che è stato introdotto in linguaggio C# versione 6 (distribuito in Visual Studio 2015).

Il resto della mia risposta è stato scritto per versioni precedenti del linguaggio C# che non avevano l'operatore ?..


In generale, se vi trovate in una situazione in cui si accede a una proprietà profondamente "annidata", come ad esempio outermostObject.a.b.c.X, probabilmente si dovrebbe prendere in considerazione ri-progettare il codice, come un tale accesso potrebbe indicare che si Stiamo violando i principi OO stabiliti (come il principio della conoscenza minima, ovvero Law of Demeter).

Alcune altre opzioni:

primo, un anti-suggestione — non farlo:

string lname = null; 
try 
{ 
    lname = Person.Name.ToLower(); 
} 
catch (NullReferenceException ex) { } // inefficient and ugly 

In secondo luogo, usando qualcosa come una monade Maybe — è possibile definire un tale scrivi te stesso È fondamentalmente un Nullable<T> che implementa IEnumerable<T> in modo che restituisca una sequenza vuota quando non è impostato alcun valore o una sequenza di esattamente un elemento se è impostato un valore. Farebbe quindi utilizzarlo come segue:

Maybe<string> personName = person.Name; 
var lname = (from name in personName select name.ToLower()).FirstOrDefault(); 

Terzo, e probabilmente la soluzione più semplice e pratico, come suggerito da ulrichb:

var lname = person.Name != null ? person.Name.ToLower() : null; 

P.S., dal momento che siamo già in tema di controllo per null, non dimenticate di controllare se person è null prima di accedere la proprietà Name ... ;-)