Perché non posso utilizzare le espressioni lambda durante il debug nella finestra "Quick watch"?
UPD: vedi anche
http://blogs.msdn.com/b/jaredpar/archive/2009/08/26/why-no-linq-in-debugger-windows.aspx
Perché non posso utilizzare le espressioni lambda durante il debug nella finestra "Quick watch"?
UPD: vedi anche
http://blogs.msdn.com/b/jaredpar/archive/2009/08/26/why-no-linq-in-debugger-windows.aspx
Risposte:
Le espressioni lambda, come i metodi anonimi, sono in realtà bestie molto complesse. Anche se escludiamo Expression
(.NET 3.5), ciò lascia ancora molta complessità, non ultimo l'essere variabili catturate, che fondamentalmente ri-strutturano il codice che le utilizza (ciò che si pensa come variabili diventano campi su classi generate dal compilatore) , con un po 'di fumo e specchi.
In quanto tale, non sono per nulla sorpreso che tu non possa usarli pigramente: c'è molto lavoro del compilatore (e la generazione di tipi dietro le quinte) che supporta questa magia.
No, non puoi usare espressioni lambda nella finestra watch / locals / immediate. Come ha sottolineato Marc, questo è incredibilmente complesso. Tuttavia, volevo approfondire l'argomento.
Ciò che la maggior parte delle persone non considera nell'esecuzione di una funzione anonima nel debugger è che non si verifica in un vuoto. L'atto stesso di definire ed eseguire una funzione anonima cambia la struttura sottostante della base di codice. Cambiare il codice, in generale, e in particolare dalla finestra immediata, è un compito molto difficile.
Considera il codice seguente.
void Example() {
var v1 = 42;
var v2 = 56;
Func<int> func1 = () => v1;
System.Diagnostics.Debugger.Break();
var v3 = v1 + v2;
}
Questo particolare codice crea una singola chiusura per acquisire il valore v1. L'acquisizione della chiusura è richiesta ogni volta che una funzione anonima utilizza una variabile dichiarata al di fuori del suo ambito. A tutti gli effetti, la v1 non esiste più in questa funzione. L'ultima riga in realtà è più simile alla seguente
var v3 = closure1.v1 + v2;
Se la funzione Example viene eseguita nel debugger, si fermerà alla riga di interruzione. Ora immagina se l'utente ha digitato quanto segue nella finestra di controllo
(Func<int>)(() => v2);
Per eseguire correttamente ciò, il debugger (o più appropriato l'EE) dovrebbe creare una chiusura per la variabile v2. Questo è difficile ma non impossibile.
Ciò che rende davvero questo un lavoro duro per l'EE è l'ultima riga. Come dovrebbe essere eseguita quella linea ora? A tutti gli effetti la funzione anonima ha cancellato la variabile v2 e l'ha sostituita con closing2.v2. Quindi l'ultima riga di codice ora ha davvero bisogno di essere letta
var v3 = closure1.v1 + closure2.v2;
Tuttavia, per ottenere effettivamente questo effetto nel codice è necessario che l'EE modifichi l'ultima riga di codice che è in realtà un'azione ENC. Sebbene questo esempio specifico sia possibile, una buona parte degli scenari non lo è.
La cosa peggiore è che l'esecuzione dell'espressione lambda non dovrebbe creare una nuova chiusura. Dovrebbe effettivamente aggiungere dati alla chiusura originale. A questo punto corri dritto nelle limitazioni ENC.
Il mio piccolo esempio purtroppo graffia solo la superficie dei problemi in cui ci imbattiamo. Continuo a dire che scriverò un intero post sul blog su questo argomento e spero di avere tempo questo fine settimana.
Non è possibile utilizzare espressioni lambda nelle finestre Immediate o Watch.
È tuttavia possibile utilizzare le espressioni System.Linq.Dynamic , che assumono la forma .Where ("Id = @ 0", 2) - non ha l'intera gamma di metodi disponibili in Linq standard e non ha la piena potere delle espressioni lambda, ma comunque, è meglio di niente!
.Any(string predicate)
, puoi inserire qualcosa come: .Where("Id>2").Any()
nella finestra di controllo o Aggiungi a sorgente. È ottimo!
Il futuro è arrivato!
Il supporto per il debug delle espressioni lambda è stato aggiunto a Visual Studio 2015 ( anteprima al momento della scrittura).
Expression Evaluator ha dovuto essere riscritto, quindi mancano molte funzionalità: debug remoto di ASP.NET, dichiarazione di variabili nella finestra immediata, ispezione di variabili dinamiche, ecc. Anche le espressioni lambda che richiedono chiamate a funzioni native non sono attualmente supportate.
questo potrebbe aiutare: Finestra immediata estesa per Visual Studio (usa Linq, Lambda Expr nel debug)
Ti auguro il meglio, Patrick
Le espressioni Lambda non sono supportate dall'analizzatore di espressioni del debugger ... il che non sorprende poiché in fase di compilazione vengono utilizzate per creare metodi (o alberi delle espressioni) piuttosto che espressioni (dai un'occhiata in Reflector con il display impostato su .NET 2 per guardali).
Inoltre, ovviamente, potrebbero formare una chiusura, un altro intero strato di struttura.
Expression
alberi - dipende dal contesto.
In VS 2015 puoi farlo ora, questa è una delle nuove funzionalità che hanno aggiunto.
Se è ancora necessario utilizzare Visual Studio 2013, è possibile effettivamente scrivere un ciclo o un'espressione lambda nella finestra immediata utilizzando anche la finestra della console del gestore pacchetti. Nel mio caso, ho aggiunto un elenco all'inizio della funzione:
private void RemoveRoleHierarchy()
{
#if DEBUG
var departments = _unitOfWork.DepartmentRepository.GetAll().ToList();
var roleHierarchies = _unitOfWork.RoleHierarchyRepository.GetAll().ToList();
#endif
try
{
//RoleHierarchy
foreach (SchoolBo.RoleHierarchy item in _listSoRoleHierarchy.Where(r => r.BusinessKeyMatched == false))
_unitOfWork.RoleHierarchyRepository.Remove(item.Id);
_unitOfWork.Save();
}
catch (Exception e)
{
Debug.WriteLine(e.ToString());
throw;
}
}
Dov'è la mia GetAll()
funzione:
private DbSet<T> _dbSet;
public virtual IList<T> GetAll()
{
List<T> list;
IQueryable<T> dbQuery = _dbSet;
list = dbQuery
.ToList<T>();
return list;
}
Qui continuavo a ricevere il seguente errore, quindi volevo stampare tutti gli elementi nei vari repository:
InnerException {"L'istruzione DELETE è in conflitto con il vincolo REFERENCE \" FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \ ". Il conflitto si è verificato nel database \" CC_Portal_SchoolObjectModel \ ", tabella \" dbo.Department \ ", colonna 'OranizationalRoleId \ nThe. \ R l'istruzione è stata terminata. "} System.Exception {System.Data.SqlClient.SqlException}
Quindi, scopro quanti record ci sono nel repository del dipartimento eseguendolo nella finestra immediata:
_unitOfWork.DepartmentRepository.GetAll().ToList().Count
Che ha restituito 243.
Quindi, se esegui quanto segue nella console del gestore pacchetti, stampa tutti gli elementi:
PM> for($i = 0; $i -lt 243; $i++) { $a = $dte.Debugger.GetExpression("departments[$i].OrgagnizationalRoleId"); Write-Host $a.Value $i }
L'autore dell'idea può essere trovato qui
Per rispondere alla tua domanda, ecco la spiegazione ufficiale di Visual Studio Program Manager del motivo per cui non puoi farlo. In breve, perché "è davvero, davvero difficile" da implementare in VS. Ma la funzione è attualmente in corso (come aggiornato ad agosto 2014).
Consenti la valutazione delle espressioni lambda durante il debug
Aggiungi il tuo voto mentre sei lì!