Outil de «surveillance rapide» de débogage Visual Studio et expressions lambda

96

Pourquoi ne puis-je pas utiliser d'expressions lambda lors du débogage dans la fenêtre «Quick watch»?

UPD: voir aussi

http://blogs.msdn.com/b/jaredpar/archive/2009/08/26/why-no-linq-in-debugger-windows.aspx

http://blogs.msdn.com/b/jaredpar/archive/2010/06/02/why-is-linq-absent-from-debugger-windows-part-2.aspx

lak-b
la source
5
Cela a été terminé et est disponible dans l'aperçu de VS 2015. visualstudio.uservoice.com/forums/121579-visual-studio/…
Francisco d'Anconia
J'ai essayé un exemple très simple donné sur MSDN pour l'expression lambda mais cela ne fonctionne pas. J'ai l'édition d'entreprise VS 2015
Adeem
2
@ Franciscod'Anconia pour activer le support lambda dans le débogage, "Utiliser le mode de compatibilité gérée" doit être coché ( stackoverflow.com/a/36559817/818321 ) Par conséquent, vous ne pourrez pas utiliser de points d'arrêt conditionnels: blogs.msdn .microsoft.com / devops / 2013/10/16 /… et stackoverflow.com/a/35983978/818321
Nik

Réponses:

64

Les expressions lambda, comme les méthodes anonymes, sont en fait des bêtes très complexes. Même si nous excluons Expression(.NET 3.5), cela laisse encore beaucoup de complexité, notamment en ce qui concerne les variables capturées, qui restructurent fondamentalement le code qui les utilise (ce que vous considérez comme des variables deviennent des champs sur les classes générées par le compilateur) , avec un peu de fumée et de miroirs.

En tant que tel, je ne suis pas du tout surpris que vous ne puissiez pas les utiliser sans rien faire - il y a beaucoup de travail de compilation (et de génération de types dans les coulisses) qui prend en charge cette magie.

Marc Gravell
la source
91

Non, vous ne pouvez pas utiliser d'expressions lambda dans la fenêtre watch / locals / immédiate. Comme Marc l'a souligné, c'est incroyablement complexe. Je voulais cependant approfondir un peu le sujet.

Ce que la plupart des gens ne considèrent pas avec l'exécution d'une fonction anonyme dans le débogueur, c'est que cela ne se produit pas dans un vide. Le simple fait de définir et d'exécuter une fonction anonyme modifie la structure sous-jacente de la base de code. Changer le code, en général, et en particulier à partir de la fenêtre immédiate, est une tâche très difficile.

Considérez le code suivant.

void Example() {
  var v1 = 42;
  var v2 = 56; 
  Func<int> func1 = () => v1;
  System.Diagnostics.Debugger.Break();
  var v3 = v1 + v2;
}

Ce code particulier crée une seule fermeture pour capturer la valeur v1. La capture de fermeture est requise chaque fois qu'une fonction anonyme utilise une variable déclarée en dehors de sa portée. À toutes fins utiles, la v1 n'existe plus dans cette fonction. La dernière ligne ressemble plus à la suivante

var v3 = closure1.v1 + v2;

Si la fonction Exemple est exécutée dans le débogueur, elle s'arrêtera à la ligne de rupture. Imaginez maintenant si l'utilisateur a tapé ce qui suit dans la fenêtre de surveillance

(Func<int>)(() => v2);

Afin d'exécuter correctement cela, le débogueur (ou plus approprié l'EE) aurait besoin de créer une fermeture pour la variable v2. C'est difficile mais pas impossible à faire.

Ce qui rend vraiment ce travail difficile pour l'EE, c'est cette dernière ligne. Comment cette ligne devrait-elle être exécutée maintenant? À toutes fins utiles, la fonction anonyme a supprimé la variable v2 et l'a remplacée par closing2.v2. Donc, la dernière ligne de code doit maintenant être lue

var v3 = closure1.v1 + closure2.v2;

Pourtant, pour obtenir réellement cet effet dans le code, l'EE doit changer la dernière ligne de code qui est en fait une action ENC. Bien que cet exemple spécifique soit possible, une bonne partie des scénarios ne le sont pas.

Ce qui est encore pire, c'est que l'exécution de cette expression lambda ne devrait pas créer une nouvelle fermeture. Il devrait en fait ajouter des données à la fermeture d'origine. À ce stade, vous entrez directement dans les limitations ENC.

Mon petit exemple ne fait malheureusement qu'effleurer la surface des problèmes que nous rencontrons. Je n'arrête pas de dire que j'écrirai un article de blog complet sur ce sujet et j'espère avoir le temps ce week-end.

JaredPar
la source
41
Pleurnicher, pleurnicher, accepter la médiocrité, pleurnicher, pleurnicher. Le débogueur est le cœur de l'EDI, et vous l'avez cassé! Les lambdas dans la fenêtre de surveillance n'ont pas besoin de capturer quoi que ce soit. Comme tout autre code de montre, ils n'ont de sens qu'au niveau du cadre de pile particulier. (Ou bien vous capturez la variable, passez à une autre fonction avec le même nom de variable ... et quoi?) Le débogueur est censé pirater le compilateur. Fais-le fonctionner!
Aleksandr Dubinsky
2
Pourquoi ils n'autorisent pas les variables capturées sur les lambdas sur la fenêtre de surveillance. Simple et permettrait un tas de scénarios de débogage où les lambdas sont simplement utilisés dans un code vraiment fonctionnel.
Luiz Felipe
@LuizFelipe même cela est toujours une entreprise énorme . Cela nécessite que l'EE génère réellement le corps de la fonction complète pour le rappel (jusqu'à IL). L'EE ne fait rien de ce genre aujourd'hui, c'est plutôt un interprète.
JaredPar
1
@JaredPar pouvez-vous partager un article de blog sur lequel marc parle
Ehsan Sajjad
49

Vous ne pouvez pas utiliser d'expressions lambda dans les fenêtres Exécution ou Espion.

Vous pouvez cependant utiliser des expressions System.Linq.Dynamic , qui prennent la forme .Where ("Id = @ 0", 2) - il n'a pas la gamme complète des méthodes disponibles dans Linq standard, et n'a pas la pleine puissance des expressions lambda, mais quand même, c'est mieux que rien!

stusherwin
la source
2
Eh bien ... alors que les autres ont expliqué alors que ce n'était pas possible, celui-ci nous fournit au moins une solution possible. +1
Nullius
1
Juste pour clarifier, vous "Importez System.Linq.Dynamic" et ensuite dans la fenêtre de débogage vous écrivez "Où (quelque chose.AsQueryable," propriété> xyz ", rien) '
smirkingman
C'est bien. Même si vous n'obtenez pas la gamme complète des méthodes d'extension Linq, par exemple il n'y en a pas .Any(string predicate), vous pouvez mettre quelque chose comme: .Where("Id>2").Any()dans la fenêtre de surveillance ou Épingler à la source. C'est bien!
Protector one
22

L'avenir est venu!

La prise en charge du débogage des expressions lambda a été ajoutée à Visual Studio 2015 ( préversion au moment de la rédaction).

Expression Evaluator a dû être réécrit, tant de fonctionnalités manquent: débogage à distance d'ASP.NET, déclaration de variables dans la fenêtre Exécution, inspection des variables dynamiques, etc.

Athari
la source
2

Les expressions lambda ne sont pas prises en charge par l'évaluateur d'expression du débogueur ... ce qui n'est guère surprenant car au moment de la compilation, elles sont utilisées pour créer des méthodes (ou des arbres d'expression) plutôt que des expressions (regardez dans Reflector avec l'affichage basculé sur .NET 2 pour les voir).

De plus, bien sûr, ils pourraient former une fermeture, une autre couche entière de structure.

Richard
la source
Eh bien, ils pourraient créer des méthodes; ils peuvent créer des Expressionarbres - cela dépend du contexte.
Marc Gravell
1

Dans VS 2015, vous pouvez le faire maintenant, c'est l'une des nouvelles fonctionnalités qu'ils ont ajoutées.

loneshark99
la source
1

Si vous devez toujours utiliser Visual Studio 2013, vous pouvez en fait écrire une boucle ou une expression lambda dans la fenêtre immédiate en utilisant également la fenêtre de la console du gestionnaire de packages. Dans mon cas, j'ai ajouté une liste en haut de la fonction:

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;
    }
}

Où est ma GetAll()fonction:

private DbSet<T> _dbSet;

public virtual IList<T> GetAll()
{
    List<T> list;
    IQueryable<T> dbQuery = _dbSet;
    list = dbQuery
        .ToList<T>();

    return list;
}

Ici, j'ai continué à recevoir l'erreur suivante, donc je voulais imprimer tous les éléments des différents référentiels:

InnerException {"L'instruction DELETE était en conflit avec la contrainte REFERENCE \" FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \ ". Le conflit est survenu dans la base de données \" CC_Portal_SchoolObjectModel \ ", table \" dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \ ". a été interrompue. "} System.Exception {System.Data.SqlClient.SqlException}

Ensuite, je découvre combien d'enregistrements se trouvent dans le référentiel du département en exécutant ceci dans la fenêtre immédiate:

_unitOfWork.DepartmentRepository.GetAll().ToList().Count

Qui a renvoyé 243.

Ainsi, si vous exécutez ce qui suit dans la console du gestionnaire de packages, il imprime tous les éléments:

PM> for($i = 0; $i -lt 243; $i++) { $a = $dte.Debugger.GetExpression("departments[$i].OrgagnizationalRoleId"); Write-Host $a.Value $i }

L'auteur de l'idée peut être trouvé ici

user8128167
la source
1

Pour répondre à votre question, voici l'explication officielle du gestionnaire de programme Visual Studio expliquant pourquoi vous ne pouvez pas faire cela. En bref, parce que "c'est vraiment, vraiment difficile" à implémenter dans VS. Mais la fonctionnalité est actuellement en cours (mise à jour en août 2014).

Autoriser l'évaluation des expressions lambda lors du débogage

Ajoutez votre vote pendant que vous y êtes!

Francisco d'Anconia
la source