Operatore a coalescenza di proprietà per C #

9

L'operatore a coalescenza nulla in c # ti consente di accorciare il codice

  if (_mywidget == null)
     return new Widget();
  else
     return _mywidget;

Fino a:

  return _mywidget ?? new Widget();

Continuo a scoprire che un operatore utile mi piacerebbe avere in C # sarebbe uno che permettesse di restituire una proprietà di un oggetto, o qualche altro valore se l'oggetto è nullo. Quindi mi piacerebbe sostituire

  if (_mywidget == null)
     return 5;
  else
     return _mywidget.Length;

Con:

  return _mywidget.Length ??! 5;

Non posso fare a meno di pensare che ci debba essere un motivo per cui questo operatore non esiste. È un odore di codice? C'è un modo migliore per scrivere questo? (Sono a conoscenza del modello di oggetto nullo ma sembra eccessivo utilizzarlo per sostituire queste quattro righe di codice.)

    
posta Ben Fulton 26.01.2011 - 01:17
fonte

12 risposte

5

Desidero molto una funzionalità in linguaggio C # che mi permetta di fare in modo sicuro x.Prop.SomeOtherProp.ThirdProp senza dover controllare ognuno di quelli per null. In un codebase in cui ho dovuto fare molti tipi di "deep property traversals", ho scritto un metodo di estensione chiamato Navigate che ha inghiottito NullReferenceExceptions e invece ha restituito un valore predefinito. Quindi potrei fare qualcosa del genere:

var propVal = myThing.Navigate(x => x.PropOne.PropTwo.PropThree.PropFour, defaultValue);

Lo svantaggio di questo è che ha un odore di codice diverso: eccezioni ingerite. Se si voleva fare qualcosa del genere "giusto", si poteva prendere il lamdba come espressione e modificare l'espressione al volo per aggiungere i controlli nulli attorno ad ogni accessorio di proprietà. Sono andato per "veloce e sporco" e non l'ho implementato in questo modo "migliore". Ma forse, quando avrò alcuni cicli di pensiero liberi, lo rivisiterò e lo pubblicherò da qualche parte.

Per rispondere più direttamente alla tua domanda, immagino che questa non sia una caratteristica perché il costo di implementazione della funzionalità supera il vantaggio derivante dall'avere la funzione. Il team C # deve scegliere e scegliere quali funzioni mettere a fuoco, e questo non è ancora arrivato alla cima. Solo un'ipotesi, anche se non ho informazioni privilegiate.

    
risposta data 26.01.2011 - 19:50
fonte
14

Credo che sarai in grado di farlo con C # 6 in questo momento semplicemente:

return _mywidget?.Length ?? 5;

Notare l'operatore condizionale null ?. sul lato sinistro. _mywidget?.Length restituisce null se _mywidget è nullo.

Un semplice operatore ternario potrebbe essere più facile da leggere, tuttavia, come altri hanno suggerito.

    
risposta data 05.07.2016 - 00:56
fonte
9

In realtà è solo una speculazione sul motivo per cui non l'hanno fatto. Dopo tutto, non ci credo ?? era nella prima versione di C #.

Ad ogni modo, vorrei solo usare l'operatore condizionale e chiamarlo un giorno:

return (_mywidget != null) ? _mywidget.Length : 5;

Il ?? è solo una scorciatoia per l'operatore condizionale.

    
risposta data 26.01.2011 - 01:45
fonte
4

Sembra proprio l'operatore ternario:

return (_mywidget != NULL) ? _mywidget : new Widget();
    
risposta data 26.01.2011 - 01:44
fonte
3

Personalmente, penso che sia dovuto alla leggibilità. Nel primo esempio, ?? si traduce molto bene come "Ci sei ??? No, facciamolo".

Nel secondo esempio, ??! è chiaramente WTF e non significa nulla per il programmatore .... l'oggetto non esiste, quindi non posso ottenere la proprietà quindi tornerò 5. Che cosa fa significa anche? Cos'è 5? Come arriviamo alla conclusione che 5 è un buon valore?

In breve, il primo esempio ha senso ... non là, nuovo . Nel secondo, è aperto al dibattito.

    
risposta data 26.01.2011 - 01:26
fonte
2

Penso che le persone siano troppo coinvolte nel "5" che stai tornando ... forse 0 sarebbe stato meglio:)

Comunque, penso che il problema è che ??! non è in realtà un operatore autonomo. Considera cosa significherebbe:

var s = myString ??! "";

In tal caso, non ha senso: ha senso solo se l'operando di sinistra è un accesso di proprietà. O che dire di questo:

var s = Foo(myWidget.Length) ??! 0;

C'è una proprietà accessor in là, ma non credo che abbia senso (se myWidget è null , vuol dire che non chiamiamo Foo() affatto?), o è solo questo un errore?

Penso che il problema sia che non si adatta in modo naturale nella lingua come ?? fa.

    
risposta data 26.01.2011 - 09:09
fonte
1

Qualcuno aveva creato una classe di utilità che avrebbe fatto questo per te. Ma non riesco a trovarlo. Quello che ho trovato è stato qualcosa di simile sui forum MSDN (guarda la seconda risposta).

Con un po 'di lavoro, puoi estenderlo per valutare le chiamate al metodo e altre espressioni che l'esempio non supporta. Puoi anche estenderlo per accettare un valore predefinito.

    
risposta data 26.01.2011 - 17:33
fonte
1

Capisco da dove vieni. Sembra come se tutti si avvolgessero attorno all'asse con l'esempio che hai fornito. Mentre sono d'accordo sul fatto che i numeri magici sono una cattiva idea, ci sarebbe da usare per l'equivalente di un operatore di coalling null per certe proprietà.

Ad esempio, hai degli oggetti legati a una mappa e vuoi che siano alla giusta altezza. Le mappe DTED possono avere buchi nei loro dati, quindi è possibile avere un valore nullo e valori nell'intervallo da -100 a ~ 8900 metri. Potresti volere qualcosa sulla falsariga di:

mapObject.Altitude = mapObject.Coordinates.DtedAltitude ?? DEFAULT_ALTITUDE;

L'operatore di calandratura null in questo caso popolerebbe l'altitudine predefinita se le coordinate non erano ancora state impostate, o l'oggetto Coordinate non poteva caricare i dati DTED per quella posizione.

Vedo che questo è molto prezioso, ma la mia speculazione sul motivo per cui non è stata eseguita è limitata alla complessità del compilatore. Ci possono essere alcuni casi in cui il comportamento non sarebbe prevedibile.

    
risposta data 26.01.2011 - 17:51
fonte
1

Quando non ho a che fare con nidificazione profonda, ho usato questo (prima che l'operatore a coalescenza nulla introdotto in C # 6). Per me è abbastanza chiaro, ma forse perché mi sono abituato, altre persone potrebbero trovarlo confuso.

return ( _mywidget ?? new MyWidget() {length = defaultLength}).Length;

Naturalmente, questo non è sempre applicabile, poiché a volte la proprietà che è necessario accedere non può essere impostata direttamente, o quando la costruzione dell'oggetto stesso è costosa, tra le altre ragioni.

Un'altra alternativa è utilizzare il modello NullObject . Si definisce una singola istanza statica della classe con valori predefiniti ragionevoli e si usa invece.

return ( _mywidget ?? myWidget.NullInstance).Length;
    
risposta data 19.07.2017 - 22:58
fonte
0

I numeri magici di solito sono un cattivo odore. Se il 5 è un numero arbitrario, allora è meglio scriverlo in modo che sia documentato in modo più chiaro. Un'eccezione a mio parere sarebbe se si dispone di una raccolta di valori che agiscono come valori predefiniti in un contesto particolare.

Se hai un insieme di valori predefiniti per un oggetto, tu potrebbe creare una sottoclasse per creare un'istanza singleton predefinita.

Qualcosa come: return (myobj ?? default_obj) .Length

    
risposta data 26.01.2011 - 07:49
fonte
0

La proprietà length di solito è un tipo di valore, quindi non può essere nullo, quindi l'operatore null-coalescing non ha senso qui.

Ma puoi farlo con una proprietà che è un tipo di riferimento, ad esempio: var window = App.Current.MainWindow ?? new Window();

    
risposta data 26.01.2011 - 17:13
fonte
-1

Ho visto qualcuno con un'idea simile prima, ma la maggior parte delle volte vorresti anche assegnare il nuovo valore nel campo null, qualcosa del tipo:

return _obj ?? (_obj = new Class());

quindi l'idea era di combinare l'assegnazione di ?? e = in:

return _obj ??= new Class();

Penso che questo abbia più senso dell'uso di un punto esclamativo.

Questa idea non è mia ma mi piace molto :))

    
risposta data 26.01.2011 - 09:23
fonte

Leggi altre domande sui tag