Por que não posso usar expressões lambda durante a depuração na janela “Quick watch”?
UPD: veja também
http://blogs.msdn.com/b/jaredpar/archive/2009/08/26/why-no-linq-in-debugger-windows.aspx
Por que não posso usar expressões lambda durante a depuração na janela “Quick watch”?
UPD: veja também
http://blogs.msdn.com/b/jaredpar/archive/2009/08/26/why-no-linq-in-debugger-windows.aspx
Respostas:
Expressões lambda, como métodos anônimos, são na verdade bestas muito complexas. Mesmo se excluirmos Expression
(.NET 3.5), isso ainda deixa muita complexidade, não menos sendo variáveis capturadas, que fundamentalmente re-estruturam o código que as usa (o que você pensa como variáveis tornam-se campos em classes geradas pelo compilador) , com um pouco de fumaça e espelhos.
Como tal, não estou nem um pouco surpreso que você não possa usá-los ociosamente - há muito trabalho do compilador (e geração de tipo nos bastidores) que suporta essa mágica.
Não, você não pode usar expressões lambda na janela watch / locals / Instant. Como Marc apontou, isso é incrivelmente complexo. Eu queria mergulhar um pouco mais fundo no assunto.
O que a maioria das pessoas não considera ao executar uma função anônima no depurador é que ela não ocorre em um vácuo. O próprio ato de definir e executar uma função anônima muda a estrutura subjacente da base do código. Alterar o código, em geral, e em particular na janela imediata, é uma tarefa muito difícil.
Considere o seguinte código.
void Example() {
var v1 = 42;
var v2 = 56;
Func<int> func1 = () => v1;
System.Diagnostics.Debugger.Break();
var v3 = v1 + v2;
}
Este código específico cria um único fechamento para capturar o valor v1. A captura de fechamento é necessária sempre que uma função anônima usa uma variável declarada fora de seu escopo. Para todos os efeitos, v1 não existe mais nesta função. A última linha se parece mais com a seguinte
var v3 = closure1.v1 + v2;
Se a função Exemplo for executada no depurador, ela irá parar na linha Break. Agora imagine se o usuário digitou o seguinte na janela do relógio
(Func<int>)(() => v2);
Para executar isso corretamente, o depurador (ou mais apropriado o EE) precisaria criar um encerramento para a variável v2. Isso é difícil, mas não impossível de fazer.
O que realmente torna este um trabalho difícil para o EE é a última linha. Como essa linha deve ser executada agora? Para todos os efeitos, a função anônima excluiu a variável v2 e substituiu-a por closure2.v2. Então, a última linha de código realmente precisa ler
var v3 = closure1.v1 + closure2.v2;
No entanto, para realmente obter esse efeito no código, é necessário que o EE altere a última linha do código, que na verdade é uma ação ENC. Embora esse exemplo específico seja possível, uma boa parte dos cenários não é.
O que é ainda pior é a execução dessa expressão lambda não deve criar um novo encerramento. Na verdade, ele deve anexar dados ao fechamento original. Neste ponto, você vai direto para as limitações ENC.
Meu pequeno exemplo, infelizmente, apenas arranha a superfície dos problemas que encontramos. Eu continuo dizendo que vou escrever um post completo no blog sobre esse assunto e espero ter tempo neste fim de semana.
Você não pode usar expressões lambda nas janelas Immediate ou Watch.
No entanto, você pode usar expressões System.Linq.Dynamic , que assumem a forma .Where ("Id = @ 0", 2) - não tem toda a gama de métodos disponíveis no Linq padrão, e não tem todos os métodos poder das expressões lambda, mas ainda assim, é melhor do que nada!
.Any(string predicate)
, você pode colocar algo como: .Where("Id>2").Any()
na janela de observação ou Fixar no código-fonte. É ótimo!
O futuro chegou!
Suporte para depuração de expressões lambda foi adicionado ao Visual Studio 2015 ( visualização no momento da escrita).
O Expression Evaluator teve que ser reescrito, então muitos recursos estão faltando: depuração remota do ASP.NET, declarando variáveis na janela Immediate, inspecionando variáveis dinâmicas, etc. Além disso, expressões lambda que requerem chamadas para funções nativas não são suportadas atualmente.
isso pode ajudar: Janela imediata estendida para Visual Studio (use Linq, Lambda Expr na depuração)
Atenciosamente, Patrick
Expressões lambda não são suportadas pelo avaliador de expressão do depurador ... o que dificilmente é surpreendente, já que em tempo de compilação elas são usadas para criar métodos (ou Árvores de Expressão) em vez de expressões (dê uma olhada no Reflector com a tela mudada para .NET 2 para vê-los).
Além disso, é claro que eles poderiam formar um fechamento, outra camada inteira de estrutura.
Expression
árvores - depende do contexto.
No VS 2015 você pode fazer isso agora, este é um dos novos recursos que eles adicionaram.
Se você ainda precisa usar o Visual Studio 2013, pode escrever um loop ou expressão lambda na janela imediata usando também a janela do console do gerenciador de pacotes. No meu caso, adicionei uma lista no topo da função:
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;
}
}
Onde minha GetAll()
função é:
private DbSet<T> _dbSet;
public virtual IList<T> GetAll()
{
List<T> list;
IQueryable<T> dbQuery = _dbSet;
list = dbQuery
.ToList<T>();
return list;
}
Aqui, recebo o seguinte erro, então queria imprimir todos os itens nos vários repositórios:
InnerException {"A instrução DELETE entrou em conflito com a restrição REFERENCE \" FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \ ". O conflito ocorreu no banco de dados \" CC_Portal_SchoolObjectModel \ ", tabela \" dbo.Department \ ", coluna 'OranizationalRoleId \". declaração foi encerrada. "} System.Exception {System.Data.SqlClient.SqlException}
Em seguida, descubro quantos registros estão no repositório do departamento, executando isto na janela imediata:
_unitOfWork.DepartmentRepository.GetAll().ToList().Count
Que retornou 243.
Portanto, se você executar o seguinte no console do gerenciador de pacotes, ele imprimirá todos os itens:
PM> for($i = 0; $i -lt 243; $i++) { $a = $dte.Debugger.GetExpression("departments[$i].OrgagnizationalRoleId"); Write-Host $a.Value $i }
O autor da ideia pode ser encontrado aqui
Para responder à sua pergunta, aqui está a explicação oficial do Visual Studio Program Manager de por que você não pode fazer isso. Resumindo, porque "é muito, muito difícil" de implementar no VS. Mas o recurso está em andamento (atualizado em agosto de 2014).
Permitir a avaliação de expressões lambda durante a depuração
Dê seu voto enquanto estiver lá!