2010-12-14 13 views
6

Rozważmy fragment poniżej:Jak pominąć funkcję z kodem lambda w środku?

[DebuggerStepThrough] 
    private A GetA(string b) 
    { 
     return this.aCollection.FirstOrDefault(a => a.b == b); 
    } 

Jeśli użyć F11 debugera nie pominąć funkcję, a nie zatrzymuje się A.b == b.

Czy istnieje jakikolwiek sposób przeskoczenia tej funkcji, zamiast korzystania z F10?

+0

Kiedy to się kończy? Klikasz go w krok na początku funkcji GetA()? Czy jest punkt przerwania? co to znaczy, że zatrzymuje się na "a.b == b"? – tster

+0

Zwykle używałbyś klawisza F10 do przechodzenia przez funkcję. Dlaczego nie chcesz użyć F10 w tym przypadku? –

+0

Zwykle używałbyś F11 podczas debugowania bez zastanawiania się, jaką kombinację klawiszy użyć F11 lub F10. DebuggerStepThrough bardzo pomaga, ale nie zawsze. – drumsta

Odpowiedz

2

widzę, dlaczego tak się dzieje, ale nie ma sposobu, aby dostać się wokół niego. Być może ktoś może na tym oprzeć. Wyrażenie lambda zostaje skompilowane do metody anonimowej.

widzę: Program.GetA.AnonymousMethod__0 (a Test)

Podobnie jak gdybyś nazwał inny sposób w metodzie masz pokazane, naciskając klawisz F11 by przejść do tej metody. np./

[DebuggerStepThrough] 
static A GetA<A>(IList<A> aCollection, string b) where A : Test 
{ 
    DoNoOp(); 
    return aCollection.FirstOrDefault(a => a.b == b); 
} 

static void DoNoOp() 
{ 
    // noop 
    Console.WriteLine("got here"); 
} 
+2

Dzięki Timowi za odpowiedź. Rozumiem też, dlaczego tak się dzieje, ponieważ lambda jest skrótem do innej funkcji. Zapytałem z nadzieją, że może mi brakować czegoś nowego w VS2010. Wygląda na to, że debugger VS2010 może być nieco ulepszony, rozszerzając opcję "Przechodzenie przez właściwości i operatorów (tylko zarządzane)". z wyrażeniami lambda. – drumsta

+0

Zgadzam się z Państwem, że wydaje się to być poważnym ograniczeniem - w międzyczasie dostarczyłem pseudo-obejście w mojej odpowiedzi poniżej, które daje pożądane rezultaty przy niewielkim ograniczeniu i potencjalną karę wykonania ... Spójrz i daj mi znać, co myślisz! – BrainSlugs83

-3

Nie jestem pewien, czy to działa, ale można spróbować umieścić atrybut w sprawie wyrażenia lambda

[DebuggerStepThrough] 
private A GetA(string b) 
{ 
    return this.aCollection.FirstOrDefault([DebuggerStepThrough]a => a.b == b); 
} 
+1

Nie działa. Klasa Attribute wiąże wstępnie zdefiniowane informacje systemowe lub niestandardowe informacje zdefiniowane przez użytkownika z elementem docelowym. Elementem docelowym może być zespół, klasa, konstruktor, delegacja, enum, zdarzenie, pole, interfejs, metoda, przenośny plik wykonywalny, parametr, właściwość, wartość zwracana, struct lub inny atrybut. – drumsta

+0

W szczególności DebuggerStepThroughAttribute ma następującą definicję użycie atrybutu: [AttributeUsage (AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Constructor, dziedziczone = false)] – drumsta

+0

Ach tak źle, mam nadzieję, ponieważ lambda jest ostatecznie anonimowy delegat za sceną może działać. Ale najwyraźniej nie można go używać na anonimowych typach. – Stefan

3

IMO to błąd w kompilatorze C#. Kompilator powinien również umieszczać te atrybuty na metodach anonimowych. Rozwiązaniem jest awaryjny do wykonania prac ręcznie, że kompilator C# robi dla Ciebie:

[DebuggerStepThrough] 
private A GetA(string b) 
{ 
    var helper = new Helper { B = b }; 

    return this.aCollection.FirstOrDefault(helper.AreBsEqual); 
} 

private class Helper 
{ 
    public string B; 

    [DebuggerStepThrough] 
    public bool AreBsEqual(A a) { return a.b == this.B; } 
} 

Ale oczywiście to jest paskudne i zupełnie nieczytelny. Właśnie dlatego kompilator C# powinien to zrobić. Ale trudne pytanie dla zespołu C# to oczywiście: które z atrybutów umieszczonych na metodzie należy skopiować do anonimowych metod wewnętrznych, a które nie?

+0

Nie zgadzam się, że to błąd. Anonimowe metody nie zawsze są prostymi wyrażeniami; mogą być długie i złożone. Nie chciałbym, żeby kompilator arbitralnie pomijał jakąkolwiek anonimową metodę lub lambdę. –

+2

@DanPuzey: Zgadzam się, że kompilator nie powinien po prostu pomijać żadnej anonimowej metody ani lambda, ale to IMO powinno, gdy sama zawierająca obiekt jest ozdobiona tym atrybutem, ponieważ o to właśnie jest ten atrybut. – Steven

+0

Ah, rozumiem - przepraszam, przegapiłem rozróżnienie. Mój błąd! –

0

Dokuczało mi to przez długi czas, dziś wymyśliłem sposób, aby to zrobić za pomocą drzewek wyrażeń w .NET 4.0.

Rozważmy następujący kod:

private class Borked 
{ 
    public object X 
    { 
     [DebuggerStepThrough] 
     get { throw new NotImplementedException(); } 
    } 
} 

private void SomeMethod() 
{ 
    var bad = new Borked(); 
    object obj = bad.TryGet(o => o.X); 
} 

Teraz - Jestem w stanie wywołać ten kod bez wahania - jedyne miejsce debugger będzie próbował powstrzymać jest łamaną własności pochłaniacza borked za - dlatego dodałem tam atrybut DebuggerStepThrough.

Zamiast przyjmować lambdę, biorę drzewo wyrażeń (które używa tej samej składni!), I kompiluję je i uruchamiam w czasie wykonywania - to jest trochę więcej pracy (ledwo) niż użycie regularna lambda, i nie będzie działać na wszystko, ale dla prostych zapytań Linq i takie, działa świetnie.

Cała magia dzieje się w następujący sposób - który, ponownie, pierwotnie używany do przyjęcia zwykłego argumentu Func<> - ale że zatrzymał debugger, gdy został zgłoszony wyjątek (mimo atrybutu krokowego), więc teraz mam zrobić z Expression<Func<>> tak:

[DebuggerStepThrough] 
public static T TryGet<OT, T>(this OT obj, params Expression<Func<OT, T>>[] getters) 
{ 
    T ret = default(T); 

    if (getters != null) 
    { 
     foreach (var getter in getters) 
     { 
      try 
      { 
       if (getter != null) 
       { 
        var getter2 = (Func<OT, T>)getter.Compile(); 
        ret = getter2(obj); 
        break; 
       } 
      } 
      catch 
      { /* try next getter or return default */ } 
     } 
    } 

    return ret; 
} 

Zgadza się - wystarczy zadzwonić .Compile() i oddanych zwracanej wartości do regularnego Func<> które można natychmiast powołać !! - Jakie to łatwe ?!

W mojej implementacji pozwoliłem użytkownikowi przekazać wiele parametrów, dzięki czemu mogą uzyskać wartość rezerwową (i że ta wartość zastępcza jest tworzona/oceniana tylko jako IF).

Nie jestem również pewien, czy przyczyną tłumienia zdarzenia debugowania jest "Just my Code" VisualStudio, czy dlatego, że jest on uruchamiany inline w tej metodzie ..., ale tak czy inaczej to działa dziwnie , cholera!!

Teraz zgaduję, że wywołanie tej metody w czasie wykonywania nie jest superszybkie, ale w praktyce nie wydaje mi się, aby w moich kwerendach dodawałem dla mnie jakiekolwiek obniżenie wydajności. Jestem bardzo podekscytowany (proszę wybaczyć wielokrotne/zbędne grzywki i interwangi itp.)

+0

Dlaczego upadły? - działa to dla pobierających (które OP próbował zrobić) - To nie działa dla seterów lub rzeczy z efektami ubocznymi - ale nadal jest to jedyne rozwiązanie, które jak dotąd zbliża się do rozwiązania problemu. (Moją nadzieją na przyszłość jest to, że ktoś dostarczy łatkę do kompilatora Roslyn.) – BrainSlugs83

Powiązane problemy