2015-12-15 4 views
6

Ho codice simile al seguente:Possibile confronto di riferimento non intenzionale lavorato come previsto

this.Session[key] = "foo"; 

if ((this.Session[key] ?? string.Empty) == "foo") 
{ 
    //do stuff 
} 

Questo, naturalmente, crea un "paragone possibile riferimento intenzionale funzionato come previsto" situazione. La soluzione a questo è ben documentata qui, e sapevo già che la soluzione era di trasmettere la variabile di sessione a string non appena ho visto il codice.

Tuttavia, il codice è vecchio di anni e non è mai stato modificato poiché è stato scritto in origine. Fino a questa settimana nel nostro ambiente di test, l'istruzione if valutata come vera ed eseguita la sezione //do stuff. Questo codice errato continua a funzionare come previsto nel nostro ambiente di produzione.

Come può essere? Non c'è ragione per cui questo codice come scritto dovrebbe mai funzionare come previsto; eppure lo ha fatto, e lo fa ancora in produzione. E cosa sarebbe cambiato per rendere questo codice che non avrebbe dovuto funzionare ma, improvvisamente, smesso di funzionare (o meglio, comportarsi come dovrebbe sempre avere)?

+0

è la sessione fuori dall'ambiente di test in ambiente di test? –

+3

Sono confuso da due cose in questa domanda. Innanzitutto, la domanda contiene un errore logico: l'azione del codice è sempre stata la stessa. Confronta due riferimenti per l'uguaglianza. Se produce vero, è perché i riferimenti sono uguali. Se produce una coerenza coerente, i riferimenti sono sempre uguali. Non capisco di cosa sei confuso qui. In secondo luogo, non posso per la vita di me capire che cosa intende intendere l'operatore a coalescenza nulla qui. Il codice significa "se il valore è nullo, confronta vuoto con null", ma perché? il valore non è già "foo" se null! –

+0

In che modo è impostata la 'Sessione [chiave]'? A cosa è paragonato? Quale versione di .NET Framework usi negli ambienti di test e di produzione? Ci deve essere una certa differenza tra i due ambienti che ha causato l'internazionalizzazione delle stringhe a funzionare in modo diverso. Il codice che hai pubblicato utilizza 2 stringhe letterali - afaik, queste saranno sempre internate a meno che l'interning non sia disattivato a livello di assembly. –

risposta

4

la stringa letterale "pippo" è interned. Significa che ogni volta che viene utilizzato, viene fatto riferimento allo stesso oggetto.

Il Common Language Runtime conserva stoccaggio stringa mantenendo una tabella, denominata pool interno, che contiene un solo riferimento a ciascuna stringa letterale unica dichiarata o creata a livello di codice nel vostro programma. Di conseguenza, un'istanza di una stringa letterale con un valore particolare esiste solo una volta nel sistema.

Ad esempio, se si assegna la stessa stringa letterale a più variabili, il runtime recupera lo stesso riferimento alla stringa letterale dal pool interno e lo assegna a ciascuna variabile.

Ecco perché object.ReferenceEquals("foo", "foo") è vero.

Se fate la stessa cosa con una stringa creata dinamicamente, non verrà internati ei riferimenti non sarà uguale

object str = new StringBuilder().Append("f").Append("oo").ToString(); // str = "foo" 
object.ReferenceEquals(str, "foo");         // false 

String internato può funzionare in modo diverso a seconda della realizzazione, è per questo che si può ottenere un comportamento diverso su ambienti diversi quando si confrontano le stringhe per riferimento.

+0

Sarebbe bene che tu abbia incluso un esempio di come farlo "correttamente" ('Object.Equals ((this.Session [chiave] ?? string.Empty)," foo ")', usando i controlli Object.Equals per i sovraccarichi della funzione Equals funziona quindi quando entrambi gli oggetti non sono i loro veri tipi e con valori null) –

+0

@ScottChamberlain Questo è un modo molto arrotondato per correggere il codice. Il modo più diretto sarebbe quello di assegnare 'Session [key]' alla stringa e utilizzare un confronto di stringhe appropriato. – phoog

+0

@phoog 'Sessione [chiave]' può o non può contenere una 'stringa'. Sono d'accordo che i programmi più sani saranno sempre lo stesso tipo ma è già stato mostrato che il codice precedente già commette errori quindi non so se ci si può fidare che non memorizzerà mai nulla tranne una stringa nella sessione variabile. –

2

Sei stato fortunato che il confronto ha funzionato! Nel tuo esempio la stringa "pippo" è interned, cioè entrambi i valori letterali di stringa sono memorizzati una volta e hanno entrambi lo stesso riferimento. Tuttavia, se uno dei due foo è definito in un altro assieme o è de-serializzato o è in qualche modo costruito in codice (ad esempio string s = "f"; string t = s + "oo";), i riferimenti potrebbero essere diversi. Poiché Session[key] viene digitato come oggetto, verrà eseguito un confronto di riferimento.

La COALESCE non è necessario, tuttavia un casting è:

if ((string)Session[key] == "foo") { ... } // This will perform a textual comparison. 

Si potrebbe anche scrivere questo (dal Equals è polimorfico):

if (Session[key].Equals("foo")) { ... } // This will perform a textual comparison. 

Non è abbastanza per confrontare 2 valori di stringa , devono essere digitati staticamente come string per essere confrontati come stringhe.

correlati, estremamente interessante posto di Jon Skeet su questo argomento: https://stackoverflow.com/a/3678810/880990

Problemi correlati