Visual Studio depura la herramienta de "vigilancia rápida" y las expresiones lambda


96

5
Esto se ha completado y está disponible en la vista previa de VS 2015. visualstudio.uservoice.com/forums/121579-visual-studio/…
Francisco d'Anconia


Intenté un ejemplo muy simple dado en MSDN para la expresión lambda pero no funciona. Tengo VS 2015 Enterprise Edition
Adeem

2
@ Franciscod'Anconia para habilitar la compatibilidad con lambda en la depuración, debe marcar "Usar modo de compatibilidad administrada" ( stackoverflow.com/a/36559817/818321 ) Como resultado, no podrá usar puntos de interrupción condicionales: blogs.msdn .microsoft.com / devops / 2013/10/16 /… y stackoverflow.com/a/35983978/818321
Nik

Respuestas:


64

Las expresiones lambda, como los métodos anónimos, son en realidad bestias muy complejas. Incluso si descartamos Expression(.NET 3.5), eso todavía deja mucha complejidad, sobre todo las variables capturadas, que fundamentalmente reestructuran el código que las usa (lo que usted piensa que las variables se convierten en campos en clases generadas por el compilador) , con un poco de humo y espejos.

Como tal, no me sorprende en lo más mínimo que no pueda usarlos sin hacer nada: hay mucho trabajo de compilación (y generación de tipos detrás de escena) que respalda esta magia.


91

No, no puede usar expresiones lambda en la ventana de observación / locales / inmediata. Como ha señalado Marc, esto es increíblemente complejo. Sin embargo, quería profundizar un poco más en el tema.

Lo que la mayoría de la gente no considera al ejecutar una función anónima en el depurador es que no ocurre en un vacío. El solo hecho de definir y ejecutar una función anónima cambia la estructura subyacente del código base. Cambiar el código, en general, y en particular desde la ventana inmediata, es una tarea muy difícil.

Considere el siguiente 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 en particular crea un cierre único para capturar el valor v1. La captura de cierre es necesaria siempre que una función anónima utilice una variable declarada fuera de su alcance. Para todos los efectos, v1 ya no existe en esta función. La última línea en realidad se parece más a la siguiente

var v3 = closure1.v1 + v2;

Si la función Example se ejecuta en el depurador, se detendrá en la línea Break. Ahora imagina si el usuario escribiera lo siguiente en la ventana de visualización

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

Para ejecutar correctamente esto, el depurador (o más apropiado el EE) necesitaría crear un cierre para la variable v2. Esto es difícil pero no imposible de hacer.

Sin embargo, lo que realmente hace que este sea un trabajo difícil para EE es la última línea. ¿Cómo debería ejecutarse esa línea ahora? Para todos los efectos, la función anónima eliminó la variable v2 y la reemplazó con closures2.v2. Entonces, la última línea de código realmente necesita leerse

var v3 = closure1.v1 + closure2.v2;

Sin embargo, para obtener realmente este efecto en el código, EE debe cambiar la última línea de código, que en realidad es una acción ENC. Si bien este ejemplo específico es posible, una buena parte de los escenarios no lo son.

Lo que es aún peor es ejecutar esa expresión lambda no debería crear un nuevo cierre. En realidad, debería agregar datos al cierre original. En este punto, corre directamente hacia las limitaciones ENC.

Desafortunadamente, mi pequeño ejemplo solo araña la superficie de los problemas con los que nos encontramos. Sigo diciendo que escribiré una publicación de blog completa sobre este tema y espero tener tiempo este fin de semana.


41
Lloriquear, lloriquear, aceptar la mediocridad, lloriquear, lloriquear. El depurador es el corazón del IDE, ¡y lo rompiste! Las lambdas en la ventana de visualización no necesitan capturar nada. Como cualquier otro código de reloj, solo tienen sentido en el marco de pila particular. (O de lo contrario, captura la variable, cambia a otra función con el mismo nombre de variable ... ¿y qué?) El depurador está destinado a piratear el compilador. ¡Hazlo funcionar!
Aleksandr Dubinsky

2
Por qué simplemente no permiten variables capturadas en lambdas en la ventana de observación. Simple y permitiría un montón de escenarios de depuración en los que las lambdas solo se utilizan en un código verdaderamente funcional.
Luiz Felipe

@LuizFelipe incluso eso sigue siendo una empresa enorme . Requiere que EE genere realmente el cuerpo de función completo para la devolución de llamada (hasta IL). El EE no hace nada de este tipo hoy, sino que es un intérprete.
JaredPar

1
@JaredPar ¿puedes compartir la publicación de blog de la que habla Marc?
Ehsan Sajjad

49

No puede usar expresiones lambda en las ventanas Inmediato o Inspección.

Sin embargo, puede usar las expresiones System.Linq.Dynamic , que toman la forma .Where ("Id = @ 0", 2) - no tiene la gama completa de métodos disponibles en Linq estándar, y no tiene el el poder de las expresiones lambda, pero aún así, ¡es mejor que nada!


2
Bueno ... mientras que los demás explicaron que no era posible, este al menos nos brinda una posible solución. +1
Nullius

1
Solo para aclarar, "Importa System.Linq.Dynamic" y luego en la ventana de depuración escribe '"Where (something.AsQueryable," property> xyz ", nothing)'
smirkingman

Esto es genial. Aunque no obtenga la gama completa de métodos de extensión Linq, por ejemplo, no hay .Any(string predicate), puede poner algo como: .Where("Id>2").Any()en la ventana de visualización o Pin to Source. ¡Es genial!
Protector uno

22

¡Ha llegado el futuro!

Se ha agregado compatibilidad para depurar expresiones lambda en Visual Studio 2015 ( vista previa en el momento de escribir este artículo).

Expression Evaluator tuvo que ser reescrito, faltan muchas características: depuración remota de ASP.NET, declarar variables en la ventana Inmediato, inspeccionar variables dinámicas, etc. Además, las expresiones lambda que requieren llamadas a funciones nativas no son compatibles actualmente.


Eso es bueno de ver. Frio...!
Rahul Nikate



2

Las expresiones lambda no son compatibles con el evaluador de expresiones del depurador ... lo cual no es sorprendente ya que en el momento de la compilación se utilizan para crear métodos (o árboles de expresión) en lugar de expresiones (eche un vistazo en Reflector con la pantalla cambiada a .NET 2 para verlos).

Además, por supuesto, podrían formar un cierre, otra capa completa de estructura.


Bueno, podrían crear métodos; pueden crear Expressionárboles, depende del contexto.
Marc Gravell

1

En VS 2015 puede hacerlo ahora, esta es una de las nuevas características que agregaron.


1

Si aún necesita usar Visual Studio 2013, puede escribir un bucle o expresión lambda en la ventana inmediata usando también la ventana de la consola del administrador de paquetes. En mi caso, agregué una lista en la parte superior de la función:

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

Donde mi GetAll()función es:

private DbSet<T> _dbSet;

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

    return list;
}

Aquí seguía recibiendo el siguiente error, así que quería imprimir todos los elementos de los distintos repositorios:

InnerException {"La declaración DELETE entró en conflicto con la restricción REFERENCE \" FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \ ". El conflicto se produjo en la base de datos \" CC_Portal_SchoolObjectModel \ ", tabla \" dbo.Department \ ", columna '\ nDelRolloResumen \". la declaración ha finalizado. "} System.Exception {System.Data.SqlClient.SqlException}

Luego, averiguo cuántos registros hay en el repositorio del departamento ejecutando esto en la ventana inmediata:

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

Que devolvió 243.

Entonces, si ejecuta lo siguiente en la consola del administrador de paquetes, imprime todos los elementos:

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

El autor de la idea se puede encontrar aquí.


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.