Narzędzie do debugowania programu Visual Studio „Quick Watch” i wyrażenia lambda


96

5
To zostało ukończone i jest dostępne w wersji zapoznawczej VS 2015. visualstudio.uservoice.com/forums/121579-visual-studio/ ...
Francisco d'Anconia


Próbowałem bardzo prostego przykładu podanego w MSDN dla wyrażenia lambda, ale to nie działa. mam wydanie Enterprise VS 2015
Adeem

2
@ Franciscod'Anconia, aby włączyć obsługę lambda w debugowaniu, należy zaznaczyć opcję „Użyj zarządzanego trybu zgodności” ( stackoverflow.com/a/36559817/818321 ). W rezultacie nie będzie można używać warunkowych punktów przerwania: blogs.msdn .microsoft.com / devops / 2013/10/16 /… i stackoverflow.com/a/35983978/818321
Nik,

Odpowiedzi:


64

Wyrażenia lambda, podobnie jak metody anonimowe, są w rzeczywistości bardzo złożonymi bestiami. Nawet jeśli wykluczymy Expression(.NET 3.5), nadal pozostawia to wiele złożoności, w szczególności bycie zmiennymi przechwytywanymi, które zasadniczo zmieniają strukturę kodu, który ich używa (to, co myślisz o zmiennych, staje się polami w klasach generowanych przez kompilator) , z odrobiną dymu i luster.

W związku z tym nie jestem ani trochę zaskoczony, że nie można ich używać bezczynnie - jest dużo pracy kompilatora (i generowanie typów za kulisami), które wspierają tę magię.


91

Nie, nie możesz używać wyrażeń lambda w oknie watch / locals / bezpośrednim. Jak zauważył Marc, jest to niezwykle złożone. Chciałem jednak zagłębić się w temat.

Większość ludzi nie bierze pod uwagę wykonywania anonimowej funkcji w debugerze, ponieważ nie występuje ona w próżni. Samo zdefiniowanie i uruchomienie anonimowej funkcji zmienia podstawową strukturę bazy kodu. Zmiana kodu w ogóle, aw szczególności z bezpośredniego okna, jest bardzo trudnym zadaniem.

Rozważmy następujący kod.

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

Ten konkretny kod tworzy pojedyncze zamknięcie, aby uchwycić wartość v1. Przechwytywanie zamknięcia jest wymagane zawsze, gdy funkcja anonimowa używa zmiennej zadeklarowanej poza jej zakresem. Do wszystkich celów i celów v1 już nie istnieje w tej funkcji. Ostatnia linia w rzeczywistości wygląda bardziej jak następująca

var v3 = closure1.v1 + v2;

Jeśli funkcja Example jest uruchomiona w debugerze, zatrzyma się na linii Break. Teraz wyobraź sobie, że użytkownik wpisał następujące polecenie w oknie zegarka

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

Aby poprawnie to wykonać, debugger (lub bardziej odpowiedni EE) musiałby utworzyć zamknięcie dla zmiennej v2. Jest to trudne, ale nie niemożliwe.

To, co naprawdę sprawia, że ​​jest to ciężka praca dla EE, to ostatnia linijka. Jak powinna teraz zostać wykonana ta linia? Z wszystkich powodów funkcja anonimowa usunęła zmienną v2 i zastąpiła ją closure2.v2. Tak więc ostatnia linia kodu naprawdę musi teraz zostać przeczytana

var v3 = closure1.v1 + closure2.v2;

Jednak aby faktycznie uzyskać ten efekt w kodzie, EE musi zmienić ostatnią linię kodu, która jest w rzeczywistości akcją ENC. Chociaż ten konkretny przykład jest możliwy, spora część scenariuszy nie.

Co gorsza, wykonanie tego wyrażenia lambda nie powinno tworzyć nowego zamknięcia. Powinien faktycznie dołączyć dane do oryginalnego zamknięcia. W tym momencie wbiegasz od razu do ograniczeń ENC.

Mój mały przykład niestety tylko zarysowuje powierzchnię problemów, z którymi się spotykamy. Wciąż powtarzam, że napiszę cały wpis na blogu na ten temat i mam nadzieję, że będę miał czas w ten weekend.


41
Jęknij, jęcz, akceptuj przeciętność, jęcz, jęcz. Debugger jest sercem IDE, a Ty go zepsułeś! Lambdy w oknie zegarka nie muszą niczego rejestrować. Jak każdy inny kod zegarka, mają one sens tylko w określonej ramce stosu. (Albo przechwytujesz zmienną, przechodzisz do innej funkcji o tej samej nazwie zmiennej ... i co?) Debugger jest przeznaczony do hakowania kompilatora. Niech to zadziała!
Aleksandr Dubinsky

2
Dlaczego po prostu nie zezwalają na przechwycone zmienne na lambdach w oknie czujki. Prosty i pozwoliłby na kilka scenariuszy debugowania, w których lambdy są po prostu używane w prawdziwie funkcjonalnym kodzie.
Luiz Felipe,

@LuizFelipe nawet to wciąż jest ogromne przedsięwzięcie . Wymaga, aby EE faktycznie wygenerował pełną treść funkcji dla wywołania zwrotnego (aż do IL). EE nie robi dziś nic takiego, zamiast tego jest tłumaczem.
JaredPar

1
@JaredPar czy możesz udostępnić wpis na blogu marc mówiący o
Ehsan Sajjad

49

Nie można używać wyrażeń lambda w oknach natychmiastowych ani obserwacyjnych.

Możesz jednak użyć wyrażeń System.Linq.Dynamic , które przyjmują postać .Where ("Id = @ 0", 2) - nie ma pełnego zakresu metod dostępnych w standardowym Linq i nie ma pełnego moc wyrażeń lambda, ale to lepsze niż nic!


2
Cóż ... podczas gdy inni wyjaśniali, chociaż nie było to możliwe, ten przynajmniej zapewnia nam możliwe rozwiązanie. +1
Nullius,

1
Dla wyjaśnienia, „Importujesz System.Linq.Dynamic”, a następnie w oknie debugowania piszesz „Gdzie (coś.AsQueryable,„ właściwość> xyz ”, nic)”
smirkingman

To jest świetne. Nawet jeśli nie dostaniesz pełną gamę metod LINQ rozszerzenie, np Nie ma .Any(string predicate), to można umieścić coś takiego: .Where("Id>2").Any()w zegarku oknie lub pin do Źródła. Wspaniale!
Protector One

22

Przyszła przyszłość!

Obsługa debugowania wyrażeń lambda została dodana do programu Visual Studio 2015 ( wersja zapoznawcza w momencie pisania).

Expression Evaluator musiało zostać przepisane, więc brakuje wielu funkcji: zdalne debugowanie ASP.NET, deklarowanie zmiennych w oknie bezpośrednim, inspekcja zmiennych dynamicznych itp. Również wyrażenia lambda, które wymagają wywołań funkcji natywnych, nie są obecnie obsługiwane.


Miło to widzieć. Chłodny...!
Rahul Nikate



2

Wyrażenia lambda nie są obsługiwane przez ewaluator wyrażeń debugera ... co nie jest zaskakujące, ponieważ w czasie kompilacji są używane do tworzenia metod (lub drzew wyrażeń), a nie wyrażeń (spójrz na Reflector z wyświetlaczem przełączonym na .NET 2, aby Zobacz ich).

I oczywiście mogą tworzyć zamknięcie, kolejną całą warstwę struktury.


Cóż, mogą tworzyć metody; mogą tworzyć Expressiondrzewa - to zależy od kontekstu.
Marc Gravell

1

W VS 2015 możesz to zrobić teraz, jest to jedna z nowych funkcji, które dodali.


1

Jeśli nadal potrzebujesz korzystać z programu Visual Studio 2013, możesz w rzeczywistości napisać pętlę lub wyrażenie lambda w bezpośrednim oknie, używając również okna konsoli menedżera pakietów. W moim przypadku dodałem listę u góry funkcji:

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

Gdzie moja GetAll()funkcja to:

private DbSet<T> _dbSet;

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

    return list;
}

Tutaj ciągle otrzymywałem następujący błąd, więc chciałem wydrukować wszystkie elementy w różnych repozytoriach:

InnerException {"Instrukcja DELETE spowodowała konflikt z ograniczeniem REFERENCE \" FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \ ". Konflikt wystąpił w bazie danych \" CC_Portal_SchoolObjectModel \ ", tabela \" dbo.Department \ ", kolumna„ OranizationalRoleId \ n ”. instrukcja została zakończona. "} System.Exception {System.Data.SqlClient.SqlException}

Następnie sprawdzam, ile rekordów znajduje się w repozytorium działu, wykonując to w bezpośrednim oknie:

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

Który zwrócił 243.

Tak więc, jeśli wykonasz następujące czynności w konsoli menedżera pakietów, wypisze wszystkie elementy:

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

Autora pomysłu można znaleźć tutaj


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.