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


5
これは完了しており、VS 2015プレビューで利用できます。visualstudio.uservoice.com/forums/121579-visual-studio/...
フランシスコ・ドールAnconia


MSDNでラムダ式の非常に単純な例を試してみましたが、機能しません。私はVS 2015エンタープライズ版を持っています
Adeem

2
@ Franciscod'Anconiaは、デバッグでラムダサポートを有効にするため、「マネージ互換モードを使用する」をオフにする必要があります(stackoverflow.com/a/36559817/818321)その結果、条件付きブレークポイントを使用できなくなります:blogs.msdn .microsoft.com / devops / 2013/10/16 /…およびstackoverflow.com/a/35983978/818321
Nik

回答:


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変数を削除し、それをクロージャ2.v2に置き換えました。したがって、コードの最後の行は本当に読む必要があります

var v3 = closure1.v1 + closure2.v2;

ただし、実際にコードでこの効果を得るには、実際にはENCアクションであるコードの最終行を変更する必要があります。この特定の例は可能ですが、シナリオの大部分は不可能です。

さらに悪いのは、ラムダ式が新しいクロージャーを作成してはならないということです。実際には、元のクロージャーにデータを追加する必要があります。この時点で、制限ENCにまっすぐ進みます。

私の小さな例は、残念なことに、私たちが遭遇する問題の表面をなぞっただけです。私はこの件について完全なブログ記事を書くつもりであり、うまくいけば私は今週末に時間があるであろう。


41
泣き言、泣き声、平凡さを受け入れる、泣き声、泣き声。デバッガはIDEの中心であり、あなたはそれを壊しました!ウォッチウィンドウのラムダは何もキャプチャする必要はありません。他のウォッチコードと同様に、特定のスタックフレームでのみ意味があります。(または、変数をキャプチャして、同じ変数名を持つ別の関数に移動します...そして何ですか?)デバッガーはコンパイラーをハックするためのものです。うまくいきましょう!
Aleksandr Dubinsky 2013年

2
なぜそれらが単純なため、ウォッチウィンドウのラムダでキャプチャされた変数が許可されません。シンプルで、ラムダが実際に機能するコードで使用されている多くのデバッグシナリオを可能にします。
Luiz Felipe

@LuizFelipeはそれでもまだ大規模な取り組みです。これは、EEが実際にコールバック(ILに至るまで)のために完全な関数本体を生成することを必要とします。EEは今日この種のことは何もせず、代わりにインタープリターです。
JaredPar 2014年

1
@JaredParは、マルクが話しているブログ投稿を共有できますか
Ehsan Sajjad

49

イミディエイトウィンドウまたはウォッチウィンドウでラムダ式を使用することはできません。

ただし、.Where( "Id = @ 0"、2)という形式のSystem.Linq.Dynamic式を使用することもできます。これには、標準のLinqで使用できるすべてのメソッドがなく、完全なメソッドもありません。ラムダ式の力ですが、それでも何もないよりはましです!


2
まあ...それは不可能だった間に他の人が説明しましたが、これは少なくとも私たちに可能な解決策を提供します。+1
ヌリウス2013

1
明確にするために、「System.Linq.Dynamicをインポート」し、デバッグウィンドウで「 "Where(something.AsQueryable、" property> xyz "、nothing)」と書きます
smirkingman

これは素晴らしい。あなたが例えば何があり、LINQの拡張メソッドの完全な範囲を取得しませんが.Any(string predicate)、あなたがすることができます:何か入れ.Where("Id>2").Any()ソースにウォッチウィンドウ、またはピンでを。それは素晴らしい!
プロテクター1

22

未来が来た!

ラムダ式のデバッグのサポートがVisual Studio 2015(執筆時のプレビュー)に追加されました

式エバリュエーターを書き直す必要があったため、ASP.NETのリモートデバッグ、イミディエイトウィンドウでの変数の宣言、動的変数の検査など、多くの機能がありません。また、ネイティブ関数の呼び出しを必要とするラムダ式も現在サポートされていません。


見てよかったです。涼しい...!
Rahul Nikate、2015年


5

これは役立つかもしれません:Visual Studioの拡張イミディエイトウィンドウ(デバッグにはLinq、Lambda Exprを使用)

最高です、パトリック


5
最初のリンクはすばらしく見えますが、それはアルファ版であり、それから出る可能性は低いことに注意してください(2008年に最終更新)。
John Salvatier、2010

2

ラムダ式は、デバッガーの式エバリュエーターではサポートされていません...コンパイル時に式ではなくメソッド(または式ツリー)を作成するために使用されるため、これは驚くには当たりません(表示を.NET 2に切り替えてReflectorを確認してください)彼らを見て)。

もちろん、それらはクロージャーを形成することもできます。


まあ、彼らメソッドを作成するかもしれません。彼らはExpression木を作成するかもしれません-それは文脈に依存します。
Marc Gravell

1

VS 2015では、今すぐ実行できます。これは、彼らが追加した新機能の1つです。


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 \ "、列 'OranizationalRoleId'。\ r \ nで発生しましたステートメントが終了しました。 "} 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 }

アイデアの作者はここにあります


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.