web-dev-qa-db-fra.com

Visual Studio débogage de l'outil "quick watch" et des expressions lambda

94
lak-b

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 un lot de complexité, notamment des variables capturées, qui restructurent fondamentalement le code qui les utilise (ce qui vous pensez que les 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 inutilement - il y a beaucoup de travail de compilation (et de génération de type dans les coulisses) qui prend en charge cette magie.

63
Marc Gravell

Non, vous ne pouvez pas utiliser d'expressions lambda dans la fenêtre watch/locals/immediate. 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 qu'elle ne se produit pas dans un vide. L'acte même 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 depuis 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 fermeture unique 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 Example 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) devrait créer une fermeture pour la variable v2. C'est difficile mais pas impossible à faire.

Ce qui en fait vraiment un 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 fermeture2.v2. Donc, la dernière ligne de code doit vraiment être lue

var v3 = closure1.v1 + closure2.v2;

Pourtant, pour obtenir réellement cet effet dans le code, l'EE doit modifier 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 exécutez directement les limitations ENC.

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

91
JaredPar

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

Vous pouvez cependant utiliser expressions System.Linq.Dynamic , qui prennent la forme. Où ("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!

49
stusherwin

L'avenir est venu!

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

Expression Evaluator a dû être réécrit, de nombreuses fonctionnalités manquent: débogage à distance ASP.NET, déclaration de variables dans la fenêtre Exécution, inspection des variables dynamiques, etc. Les expressions lambda qui nécessitent des appels à des fonctions natives ne sont pas actuellement prises en charge.

21
Athari

cela pourrait aider: Fenêtre immédiate étendue pour Visual Studio (utilisez Linq, Lambda Expr dans le débogage)

Cordialement, Patrick

5
Patrick Wolf

Les expressions lambda ne sont pas prises en charge par l'évaluateur d'expression du débogueur ... ce qui n'est pas surprenant puisqu'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 (jetez un œil 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.

2
Richard

Si vous devez toujours utiliser Visual Studio 2013, vous pouvez réellement écrire une boucle ou une expression lambda dans la fenêtre immédiate en utilisant également la fenêtre de 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ù ma fonction GetAll() est:

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é à obtenir l'erreur suivante, donc je voulais imprimer tous les éléments dans les différents référentiels:

InnerException {"L'instruction DELETE est 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\", colonne\n\The\'d \'. est terminée. "} System.Exception {System.Data.SqlClient.SqlException}

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

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

Qui a rendu 243.

Donc, 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

1
user8128167

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

1
loneshark99

Pour répondre à votre question, voici l'explication officielle du Gestionnaire de programmes Visual Studio expliquant pourquoi vous ne pouvez pas le faire. 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!

1
Francisco d'Anconia