c# visual-studio debugging lambda

c# - Herramienta de "reloj rápido" de depuración de Visual Studio y expresiones lambda



visual-studio debugging (9)

¡El futuro ha llegado!

Soporte para la depuración de expresiones lambda se ha agregado a Visual Studio 2015 ( Vista previa en el momento de la escritura).

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


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


Las expresiones Lambda no son compatibles con el evaluador de expresiones del depurador ... lo cual no es sorprendente ya que en tiempo de compilación se usan 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 a verlas).

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


No puede usar expresiones lambda en las ventanas Inmediato o Watch.

Sin embargo, puede usar 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 completo poder de expresiones lambda, pero aún así, ¡es mejor que nada!


No, no puede usar expresiones lambda en la ventana watch / locals / immediate. Como Marc ha señalado, esto es increíblemente complejo. Sin embargo, quería adentrarme 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 de la base de código. Cambiar el código, en general, y en particular desde la ventana inmediata, es una tarea muy difícil.

Considera 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 solo cierre para capturar el valor v1. Se requiere captura de cierre siempre que una función anónima use 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 Ejemplo se ejecuta en el depurador, se detendrá en la línea de corte. Ahora imagine si el usuario escribió lo siguiente en la ventana del reloj

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

Para ejecutar esto correctamente, 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 esa última línea. ¿Cómo debería ser ejecutada esa línea ahora? Para todos los efectos, la función anónima borró la variable v2 y la reemplazó con closure2.v2. Entonces la última línea de código realmente necesita leer

var v3 = closure1.v1 + closure2.v2;

Sin embargo, para obtener este efecto en el código, el 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 estar creando un nuevo cierre. En realidad, debería estar agregando datos al cierre original. En este punto, se ejecuta directamente en las limitaciones ENC.

Mi pequeño ejemplo desafortunadamente solo araña la superficie de los problemas que encontramos. Sigo diciendo que escribiré una publicación completa en este tema y espero tener tiempo este fin de semana.


Para responder a su pregunta, esta es la explicación oficial del Administrador de programas de Visual Studio de por qué no puede hacer esto. En resumen, porque "es muy, muy difícil" de implementar en VS. Pero la característica está actualmente en progreso (como se actualizó en agosto de 2014).

visualstudio.uservoice.com/forums/121579-visual-studio/…

¡Agregue su voto mientras está allí!


Si aún necesita usar Visual Studio 2013, puede escribir un bucle o una expresión lambda en la ventana inmediata utilizando 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 función GetAll() 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í recibiendo el siguiente error, así que quería imprimir todos los elementos en los diferentes repositorios:

InnerException {"La instrucción DELETE entró en conflicto con la restricción de REFERENCIA /" FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId / ". El conflicto ocurrió en la base de datos /" CC_Portal_SchoolObjectModel / ", table /" dbo.Department / ", column ''OranizationalRoleId''. / R / nThe declaración ha terminado. "} System.Exception {System.Data.SqlClient.SqlException}

Luego, descubro cuántos registros hay en el repositorio del departamento al ejecutar esto en la ventana inmediata:

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

Que devolvió 243.

Por lo tanto, 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 here



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

Como tal, no estoy en lo más mínimo sorprendido de que no puedas usarlos ociosamente; hay mucho trabajo de compilación (y generación de tipos entre bastidores) que respalda esta magia.