Инструмент для быстрой отладки Visual Studio и лямбда-выражения

96

Почему я не могу использовать лямбда-выражения при отладке в окне «Быстрый просмотр»?

UPD: смотрите также

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
источник
5
Это завершено и доступно в предварительной версии VS 2015. visualstudio.uservoice.com/forums/121579-visual-studio/…
Francisco d'Anconia
Я пробовал очень простой пример, приведенный в MSDN для лямбда-выражения, но он не работает. У меня VS 2015 Enterprise Edition
Adeem
2
@ Franciscod'Anconia, чтобы включить поддержку лямбда-выражения при отладке, необходимо отключить «Использовать управляемый режим совместимости» ( stackoverflow.com/a/36559817/818321 ). В результате вы не сможете использовать условные точки останова: blogs.msdn .microsoft.com / DevOps / 2013/10/16 /… и stackoverflow.com/a/35983978/818321
Ник,

Ответы:

64

Лямбда-выражения, как и анонимные методы, на самом деле очень сложные звери. Даже если мы исключим Expression(.NET 3.5), все равно останется много сложностей, не в последнюю очередь из-за захваченных переменных, которые фундаментально реструктурируют код, который их использует (то, что вы думаете, как переменные, становится полями в классах, созданных компилятором) , немного дыма и зеркал.

Таким образом, я ни в малейшей степени не удивлен, что вы не можете использовать их праздно - есть много работы компилятора (и генерации типов за кулисами), которая поддерживает эту магию.

Марк Гравелл
источник
91

Нет, вы не можете использовать лямбда-выражения в окне просмотра / локальных / немедленных действий. Как заметил Марк, это невероятно сложно. Однако я хотел немного углубиться в эту тему.

Что большинство людей не учитывает при выполнении анонимной функции в отладчике, так это то, что это не происходит в вакууме. Сам процесс определения и запуска анонимной функции изменяет базовую структуру базы кода. Изменение кода в целом и, в частности, из непосредственного окна - очень сложная задача.

Рассмотрим следующий код.

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

Этот конкретный код создает одно закрытие для захвата значения v1. Захват закрытия требуется всякий раз, когда анонимная функция использует переменную, объявленную вне ее области видимости. Фактически v1 больше не существует в этой функции. Последняя строка больше похожа на следующую

var v3 = closure1.v1 + v2;

Если в отладчике запущена функция Example, она остановится на строке Break. А теперь представьте, если бы пользователь ввел в окно просмотра следующее:

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

Чтобы правильно выполнить это, отладчику (или, что более уместно, EE) потребуется создать закрытие для переменной v2. Это сложно, но возможно.

Что действительно затрудняет эту работу для EE, так это последняя строчка. Как теперь выполнить эту строку? По сути, анонимная функция удалила переменную v2 и заменила ее на closure2.v2. Итак, последняя строка кода действительно должна быть прочитана

var v3 = closure1.v1 + closure2.v2;

Тем не менее, чтобы получить этот эффект в коде, требуется, чтобы EE изменил последнюю строку кода, которая на самом деле является действием ENC. Хотя этот конкретный пример возможен, большая часть сценариев - нет.

Что еще хуже, выполнение этого лямбда-выражения не должно создавать нового замыкания. На самом деле он должен добавлять данные к исходному закрытию. На этом этапе вы сразу сталкиваетесь с ограничениями ENC.

К сожалению, мой небольшой пример лишь поверхностно затрагивает проблемы, с которыми мы сталкиваемся. Я все время говорю, что напишу полный пост в блоге на эту тему и, надеюсь, у меня будет время в эти выходные.

ДжаредПар
источник
41
Хныкать, ныть, принимать заурядность, ныть, ныть. Отладчик - это сердце IDE, и вы его сломали! Лямбды в окне просмотра не должны ничего захватывать. Как и любой другой код наблюдения, они имеют смысл только в конкретном кадре стека. (Или вы захватите переменную, перейдете к другой функции с тем же именем переменной ... и что?) Отладчик предназначен для обхода компилятора. Сделай так, чтобы это работало!
Александр Дубинский
2
Почему они просто не разрешают захваченные переменные в лямбдах в окне просмотра. Простой и допускает множество сценариев отладки, в которых лямбды просто используются в действительно функциональном коде.
Луис Фелипе
@LuizFelipe даже это еще массивное под принятием. Для этого требуется, чтобы EE фактически сгенерировал полное тело функции для обратного вызова (вплоть до IL). EE сегодня ничего подобного не делает, это интерпретатор.
ДжаредПар
1
@JaredPar, можешь ли ты поделиться записью в блоге, о которой говорит Марк
Эхсан Саджад
49

Вы не можете использовать лямбда-выражения в окнах Immediate или Watch.

Однако вы можете использовать выражения System.Linq.Dynamic , которые имеют форму .Where ("Id = @ 0", 2) - у него нет полного набора методов, доступных в стандартном Linq, и нет полного сила лямбда-выражений, но все же это лучше, чем ничего!

Stusherwin
источник
2
Ну ... в то время как другие объяснили, хотя это было невозможно, этот, по крайней мере, предоставляет нам возможное решение. +1
Нуллий
1
Чтобы уточнить, вы «Импортируете System.Linq.Dynamic», а затем в окне отладки пишете «Где (something.AsQueryable,« свойство> xyz », ничего)»
ухмыляющийся человек
Это круто. Даже если у вас нет полного набора методов расширения Linq, например, нет .Any(string predicate), вы можете поместить что-то вроде: .Where("Id>2").Any()в окно просмотра или закрепить в исходном коде. Здорово!
Protector one
22

Будущее наступило!

Поддержка отладки лямбда-выражений была добавлена ​​в Visual Studio 2015 ( предварительная версия на момент написания).

Оценщик выражений пришлось переписать, поэтому многие функции отсутствуют: удаленная отладка ASP.NET, объявление переменных в окне Immediate, проверка динамических переменных и т. Д. Также в настоящее время не поддерживаются лямбда-выражения, требующие вызовов собственных функций.

Атари
источник
5

это может помочь: Расширенное немедленное окно для Visual Studio (используйте Linq, Lambda Expr при отладке)

Всего наилучшего, Патрик

Патрик Вольф
источник
5
Обратите внимание, что хотя первая ссылка выглядит потрясающе, она находится в альфа-версии и вряд ли когда-нибудь выйдет из нее (последнее обновление в 2008 году).
Джон Сальватье
2

Лямбда-выражения не поддерживаются оценщиком выражений отладчика ... что неудивительно, поскольку во время компиляции они используются для создания методов (или деревьев выражений), а не выражений (посмотрите в Reflector с отображением, переключенным на .NET 2, чтобы увидеть их).

Плюс, конечно, они могут образовывать закрытие, еще один слой структуры.

Ричард
источник
Что ж, они могут создавать методы; они могут создавать Expressionдеревья - это зависит от контекста.
Марк Грейвелл
1

В VS 2015 вы можете сделать это прямо сейчас, это одна из новых функций, которые они добавили.

loneshark99
источник
1

Если вам все еще нужно использовать Visual Studio 2013, вы можете написать цикл или лямбда-выражение в непосредственном окне, используя также окно консоли диспетчера пакетов. В моем случае я добавил список вверху функции:

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

Где моя GetAll()функция:

private DbSet<T> _dbSet;

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

    return list;
}

Здесь я продолжал получать следующую ошибку, поэтому я хотел распечатать все элементы в различных репозиториях:

InnerException {"Оператор DELETE вступил в конфликт с ограничением REFERENCE \" FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \ ". Конфликт произошел в базе данных \" CC_Portal_SchoolObjectModel \ ", таблица \" dbo.Department \ ", столбец 'OranizationalRoleI \ nThe оператор был прерван. "} System.Exception {System.Data.SqlClient.SqlException}

Затем я узнаю, сколько записей находится в репозитории отдела, выполнив это в ближайшем окне:

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

Что вернуло 243.

Итак, если вы выполните следующее в консоли диспетчера пакетов, он распечатает все элементы:

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

Автор идеи можно найти здесь

user8128167
источник
1

Чтобы ответить на ваш вопрос, вот официальное объяснение Visual Studio Program Manager, почему вы не можете этого сделать. Короче говоря, потому что «это действительно очень сложно» реализовать в VS. Но эта функция в настоящее время находится в стадии разработки (по состоянию на август 2014 г.).

Разрешить оценку лямбда-выражений во время отладки

Добавьте свой голос, пока вы там!

Франсиско д'Анкония
источник