2012-06-16 13 views
18

Sono interessato a recuperare i nomi delle variabili locali (e dei parametri) in fase di esecuzione in modo sicuro. Ho il seguente metodo di estensione:Acquisizione dei nomi di variabili locali (e parametri) in fase di esecuzione tramite espressioni lambda

public static string GetVariableName<T>(Expression<Func<T>> variableAccessExpression) 
{ 
    var memberExpression = variableAccessExpression.Body as MemberExpression; 
    return memberExpression.Member.Name; 
} 

... che restituisce il nome della variabile catturata attraverso un'espressione lambda:

static void Main(string[] args) 
{ 
    Console.WriteLine(GetVariableName(() => args)); 
    // Output: "args" 

    int num = 0; 
    Console.WriteLine(GetVariableName(() => num)); 
    // Output: "num" 
} 

Tuttavia, questo funziona solo perché il compilatore C# promuove tutte le variabili locali (e parametri) che vengono catturati in funzioni anonime per variabili di istanza con lo stesso nome all'interno di una classe generata dal compilatore dietro le quinte (per Jon Skeet). In caso contrario, il cast di Body a MemberExpression non funzionerà, poiché MemberExpression rappresenta l'accesso al campo o alla proprietà.

È questo comportamento di modifica delle variabili documentato o è un dettaglio di implementazione soggetto a modifiche in altre versioni del framework?

Nota: questa domanda è una generalizzazione di my former one on argument validation.

+0

È possibile rilevare le alternative qui: http://stackoverflow.com/questions/72121/finding-the-variable-name-passed-to-a-function-in-c-sharp ma di nuovo non documentato. L'approccio di tipo anonimo è molto più veloce anche se .. – nawfal

risposta

1

AFAIK, è un dettaglio di implementazione.

Tuttavia, penso che si possa scommettere che in realtà non cambierà.

Ho appena testato su VS2012 RC e funziona come previsto, quindi sei al sicuro per almeno un paio d'anni.

+1

Che dire di Mono e di implementazioni alternative del framework? Sono stati conosciuti per prendere alcune libertà con i dettagli di implementazione (per esempio, forniscono [semiconduttori più lenti sul modello di memoria] (http://stackoverflow.com/a/10608993/1149773) rispetto a .NET Framework). Anche se oggi utilizzassero la stessa implementazione, potrebbero non farlo nelle versioni future. – Douglas

+1

Ancora, grazie per il controllo su VS2012 :-) Buono a sapersi che almeno Microsoft non lo romperà ancora. – Douglas

+0

Non so nulla di Mono - dovresti controllare. È davvero un disastro: pensi che la possibilità che la tua soluzione si interrompa è maggiore della possibilità di ottenere errori umani con 'CheckNotNull (" args ", args)' –

4

Questo è un comportamento dovresti non fare affidamento su.

Date un'occhiata a Abuse of C# lambda expressions or Syntax brilliance?

Ora leggere i commenti da Eric Lippert, che è parte del team di progettazione C#. Includono:

Ho appena chiesto ad Anders (e al resto del team di progettazione) cosa pensassero . Diciamo solo che i risultati non sarebbero stampabile in un giornale adatto alle famiglie

e

Per quanto riguarda il motivo per cui questo è orrendo, potremmo cominciare con il non ovvio, intelligente (ricordate, intelligente è male , il codice intelligente è difficile da mantenere), non è affatto nei casi di utilizzo di by-progettuali previste dai progettisti di lambda, lenta, fragile, portabile, e inutile

Da queste affermazioni direi che non diventerà un comportamento documentato o supportato.

+0

+1: questo mi ha indirizzato nella giusta direzione su quale sarebbe stata la risposta. Tuttavia, nella domanda collegata, il '.Attributes (style =>" width: 100% ")' lambda sembra essere usato solo perché sembra esteticamente/stilisticamente migliore del tradizionale '.Attributes (" style "," width: 100% ")" sintassi, e non perché introduce qualsiasi funzionalità altrimenti non disponibile, giustificando il commento di Lippert che è "non necessario". (continua ...) – Douglas

+3

Nella mia domanda, ritengo che ci sia un caso d'uso più forte per ottenere il nome membro/variabile usando lambdas. Trovo che la pratica .NET di incorporare i nomi di membri/variabili in stringhe - come ad esempio 'PropertyChangedEventArgs' e' ArgumentException' - sia più "orrenda" della mia alternativa proposta. – Douglas

+0

Prism4 utilizza questa stessa cosa per il sovraccarico dell'albero di espressione di RaisePropertyChanged. – JKor

12

Aggiornamento: Questo non è più un problema da C# 6, che ha introdotto l'operatore nameof per affrontare tali scenari (vedi MSDN).

Sembra che la risposta alla mia domanda è non; la funzione non è standardizzata. La situazione sembra ancora più cupa di quanto avessi inizialmente sospettato; non solo la promozione delle variabili catturate non è standardizzata, ma lo è anche l'intera specifica della conversione di funzioni anonime alle loro rappresentazioni di alberi di espressione.

L'implicazione di questo è che le funzioni anonime anche semplici, come il basso, non si garantisce che producono strutture di espressione coerenti tra diverse implementazioni del quadro (fino a quando la conversione è standardizzata):

Expression<Func<int, int, int>> add = (int x, int y) => x + y; 

I seguenti estratti sono presi dallo C# Language Specification 4.0 (enfasi aggiunta in tutti i casi).

Da “tipi di alberi 4.6 di espressione”:

La definizione esatta del tipo generico Expression<D> nonché le regole precise per la costruzione di un albero di espressione quando una funzione anonima viene convertito in un tipo di albero di espressione, sono entrambi al di fuori dello scopo di questa specifica e sono descritti altrove.

Da “6.5.2 Valutazione delle conversioni funzioni anonime a tipi di alberi espressione”:

Conversione di una funzione anonima in un tipo albero di espressione produce un albero di espressione (§4.6). Più precisamente, la valutazione della conversione della funzione anonima porta alla costruzione di una struttura oggetto che rappresenta la struttura della funzione anonima stessa. La struttura precisa dell'albero delle espressioni e il processo esatto per crearlo sono definiti dall'implementazione.

Il terzo esempio in “6.5.3 esempio attuazione” dimostra la conversione di una funzione anonima che cattura una variabile locale, e conferma la promozione variabile citato nella mia interrogazione:

La durata di la variabile locale deve ora essere estesa almeno alla durata del delegato della funzione anonima. Questo può essere ottenuto "issando" la variabile locale in un campo di una classe generata dal compilatore. L'istanziazione della variabile locale (§7.15.5.2) corrisponde quindi alla creazione di un'istanza della classe generata dal compilatore e l'accesso alla variabile locale corrisponde all'accesso a un campo nell'istanza della classe generata dal compilatore.

Questo è ulteriormente confermata alla fine della sezione:

La stessa tecnica applicata qui per catturare variabili locali può essere utilizzato anche per la conversione funzioni anonime per alberi di espressione: I riferimenti agli oggetti compilatore generato può essere memorizzato nell'albero delle espressioni e l'accesso alle variabili locali è possibile rappresentarlo come accesso al campo su questi oggetti, . Il vantaggio di questo approccio è che consente di condividere le variabili locali "sollevate" tra i delegati e gli alberi di espressione.

Tuttavia, c'è un disclaimer all'inizio della sezione:

La realizzazione qui descritto si basa sugli stessi principi utilizzati dal Microsoft compilatore C#, ma è in alcun modo un implementazione obbligatoria, né è l'unica possibile. Si parla solo brevemente di conversioni in alberi di espressione, poiché la loro esatta semantica non rientra nell'ambito di questa specifica.

P.S. Eric Lippert confirms in this comment che le specifiche dell'albero dell'espressione non sono mai state spedite. Esiste un Expression Trees v2 Spec nella documentazione DLR su CodePlex, ma il suo ambito non sembra coprire la conversione di funzioni anonime in alberi di espressioni in C#.

+0

@ rally25rs: buon punto. La mia ipotesi è che Microsoft * abbia * una specifica interna che seguono per tutte le loro implementazioni, ma hanno scelto di non impegnarsi a renderlo standardizzato pubblicamente. Significa che è quasi certamente sicuro assumere che le conversioni invocate in altre parti del framework (come Linq2Sql) rimarranno coerenti; tuttavia, questa rassicurazione non si estende alle espressioni in generale (come l'espressione menzionata nella mia domanda per la lettura dei nomi dei parametri). – Douglas

Problemi correlati