2010-11-22 13 views
52

Solo per curiosità/convenienza: C# fornisce due funzioni espressione condizionale interessanti che conosco:C# se-null-poi-null espressione

string trimmed = (input == null) ? null : input.Trim(); 

e

string trimmed = (input ?? "").Trim(); 

mi manca un altro tale espressione per una situazione che affronto molto spesso:

Se il riferimento di input è nullo, l'output deve essere nullo. In caso contrario, l'output dovrebbe essere il risultato dell'accesso a un metodo o una proprietà dell'oggetto di input.

Ho fatto esattamente questo nel mio primo esempio, ma (input == null) ? null : input.Trim() è piuttosto prolisso e illeggibile.

Esiste un'altra espressione condizionale per questo caso oppure è possibile utilizzare l'operatore ?? in modo elegante?

+0

Come Jon menzionato di seguito, ora è possibile utilizzare null-conditi operatori onal che usano C# 6.0, come theText? .Trim() – Santosh

risposta

47

Qualcosa come l'operatore di dereferenziazione Null-Safe di Groovy?

string zipCode = customer?.Address?.ZipCode; 

ho capito che la squadra # C ha guardato a questo e ha scoperto che non è così semplice per la progettazione elegante come ci si potrebbe aspettare ... anche se non ho sentito parlare i dettagli dei problemi.

Non credo che al momento ci sia qualcosa di simile nella lingua, temo ... e non ho sentito nessun piano per questo, anche se questo non vuol dire che non succederà a qualche punto.

EDIT: Ora farà parte del C# 6, come "operatore condizionale nullo".

+3

Mi piacerebbe avere un operatore di dereferenziazione nullo in C# un giorno. –

+1

Danny: potresti già giocare con la dinamica usando la dinamica se accetti di perdere sia la sicurezza che la velocità del tipo. Anche l'elegante "?" la sintassi deve essere sostituita da una chiamata al metodo di estensione. Il metodo potrebbe essere chiamato AndAnd() per specchiare http://andand.rubyforge.org/ –

+1

Sembra che il team C# potrebbe prendere in prestito quell'operatore. http://channel9.msdn.com/Forums/Coffeehouse/Mads-Torgersen--NDC-London--The-Future-of-C –

9

Attualmente possiamo scrivere solo un metodo di estensione se non vuoi ripetere te stesso, temo.

public static string NullableTrim(this string s) 
{ 
    return s == null ? null : s.Trim(); 
} 
5

Non c'è niente di built-in, ma si potrebbe avvolgere il tutto in un metodo di estensione, se si voleva (anche se probabilmente non mi preoccuperei).

Per questo esempio specifico:

string trimmed = input.NullSafeTrim(); 

// ... 

public static class StringExtensions 
{ 
    public static string NullSafeTrim(this string source) 
    { 
     if (source == null) 
      return source; // or return an empty string if you prefer 

     return source.Trim(); 
    } 
} 

o una versione più general-purpose:

string trimmed = input.IfNotNull(s => s.Trim()); 

// ... 

public static class YourExtensions 
{ 
    public static TResult IfNotNull<TSource, TResult>(
     this TSource source, Func<TSource, TResult> func) 
    { 
     if (func == null) 
      throw new ArgumentNullException("func"); 

     if (source == null) 
      return source; 

     return func(source); 
    } 
} 
+0

+1 per 'NullSafeTrim'. Il 'IfNotNull' non mi convince, non riesco a vedere un guadagno in termini di leggibilità, solo un aumento della complessità del codice. – chiccodoro

+0

@chiccodoro: Non sono convinto da nessuno di questi come una soluzione a questo particolare "problema", ho solo pensato di buttarli nel mix! Probabilmente userò 'if (foo! = Null) foo = foo.Trim();' quasi ogni volta per questo tipo di situazione, anche se ci sono molte altre situazioni in cui è utile e/o necessario per poter usare un'espressione piuttosto che un'affermazione. – LukeH

4

Ho avuto lo stesso problema che ho scritto qualche piccolo metodi di estensione:

public static TResult WhenNotNull<T, TResult>(
    this T subject, 
    Func<T, TResult> expression) 
    where T : class 
{ 
    if (subject == null) return default(TResult); 
    return expression(subject); 
} 

public static TResult WhenNotNull<T, TResult>(
    this T subject, Func<T, TResult> expression, 
    TResult defaultValue) 
    where T : class 
{ 
    if (subject == null) return defaultValue; 
    return expression(subject); 
} 

public static void WhenNotNull<T>(this T subject, Action<T> expression) 
    where T : class 
{ 
    if (subject != null) 
    { 
     expression(subject); 
    } 
} 

Lo usi così;

string str = null; 
return str.WhenNotNull(x => x.Length); 

o

IEnumerable<object> list; 
return list.FirstOrDefault().WhenNotNull(x => x.id, -1); 

o

object obj; 
IOptionalStuff optional = obj as IOptionalStuff; 
optional.WhenNotNull(x => x.Do()); 

ci sono sovraccarichi anche per i tipi nullable.

+0

Sembra interessante, anche se non sono sicuro di ciò che ottieni. Per i miei occhi non è più leggibile e tecnicamente introduce più complessità nel codice compilato. – chiccodoro

+0

@chiccodoro: in alcuni casi è più leggibile, ad esempio dopo FirstOrDefault o in casi simili, dove non è necessaria un'altra variabile e nessun if. Puoi concatenarli senza usare molte variabili. Dipende anche dal fatto che tu permetta di scrivere se è in una riga o se costringi i tuoi sviluppatori a scriverlo in quattro righe. –

7

Come soluzione temporanea è possibile utilizzare ciò che è basato su Maybe monad.

public static Tout IfNotNull<Tin, Tout>(this Tin instance, Func<Tin, Tout> Output) 
{ 
    if (instance == null) 
     return default(Tout); 
    else 
     return Output(instance); 
} 

usarlo in questo modo:

int result = objectInstance.IfNotNull(r => 5); 
var result = objectInstance.IfNotNull(r => r.DoSomething()); 
+0

Questo è davvero eccezionale: l'ho usato con Entity Framework se si accede a un'entità referenziata, ad es. 'var x = Entity1.Entity2.IfNotNull (x => x.EntityDescription) ?? string.Empty' che restituisce EntityDescription contenuto in Entity2 a cui fa riferimento Entity1 - o una stringa vuota se qualsiasi oggetto Entity1 o Entity2 è null. Senza IfNotNull hai un'espressione lunga e brutta. Molto bene! – Matt

10

è possibile scegliere tra una classe personalizzata Nullify o di un metodo NullSafe estensione come descritto qui: http://qualityofdata.com/2011/01/27/nullsafe-dereference-operator-in-c/

L'utilizzo sarà il seguente:

//Groovy: 
bossName = Employee?.Supervisor?.Manager?.Boss?.Name 

//C# Option 1: 
bossName = Nullify.Get(Employee, e => e.Supervisor, s => s.Manager, 
         m => m.Boss, b => b.Name); 
//C# Option 2: 
bossName = Employee.NullSafe(e => e.Supervisor).NullSafe(s => s.Boss) 
         .NullSafe(b => b.Name); 
+2

il metodo di estensione NullSafe è semplicemente fantastico! –

Problemi correlati