簡體   English   中英

Visual Studio調試“快速監視”工具和lambda表達式

[英]Visual Studio debugging “quick watch” tool and lambda expressions

為什么在“快速監視”窗口中調試時不能使用lambda表達式?

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

不,您不能在watch / locals /即時窗口中使用lambda表達式。 正如Marc所指出的那樣,這非常復雜。 不過,我想進一步探討這個話題。

大多數人在調試器中執行匿名函數時不會考慮的是,它不會在真空中發生。 定義和運行匿名函數的行為改變了代碼庫的基礎結構。 通常,尤其是從即時窗口更改代碼是一項非常困難的任務。

考慮下面的代碼。

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;

如果在調試器中運行示例函數,它將在中斷行處停止。 現在,假設用戶是否在監視窗口中輸入了以下內容

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

為了正確執行此操作,調試器(或更合適的EE)將需要為變量v2創建一個閉包。 這很難但並非不可能。

對於EE而言,真正使這項工作變得艱難的是最后一行。 現在應該如何執行該行? 出於所有目的和目的,匿名函數刪除了v2變量,並將其替換為closure2.v2。 所以現在實際上需要閱讀最后一行代碼

var v3 = closure1.v1 + closure2.v2;

然而,要在代碼中實際獲得這種效果,則EE必須更改最后一行代碼,這實際上是ENC動作。 盡管可以使用此特定示例,但大部分情況都不可行。

更糟糕的是執行lambda表達式不應創建新的閉包。 它實際上應該將數據附加到原始閉包中。 此時,您會直接遇到ENC的限制。

不幸的是,我的小例子只能解決我們遇到的問題。 我一直說我會寫一篇完整的博客文章,希望這個周末有時間。

Lambda表達式就像匿名方法一樣,實際上是非常復雜的野獸。 即使我們排除Expression (.NET 3.5),仍然會留下很多復雜性,尤其是被捕獲的變量,它們從根本上重新構造了使用它們的代碼(您認為變量成為編譯器生成的類中的字段) ),並帶有一點煙霧和鏡子。

因此,您可以無所事事地使用它們,我一點也不感到驚訝-有很多支持這種魔術的編譯器工作(以及幕后的類型生成)。

您不能在“即時”或“監視”窗口中使用lambda表達式。

但是,您可以使用System.Linq.Dynamic表達式 ,其形式為.Where(“ Id = @ 0”,2)-它沒有標准Linq中可用的全部方法,也沒有完整的方法Lambda表達式的強大功能,但總比沒有好!

未來來了!

對調試lambda表達式的支持已添加到Visual Studio 2015 (在撰寫本文時為預覽 )。

Expression Evaluator必須重寫,因此缺少許多功能:遠程調試ASP.NET,在Instant窗口中聲明變量,檢查動態變量等。目前還不支持需要調用本機函數的lambda表達式。

這可能會有所幫助:Visual Studio的擴展即時窗口(在調試中使用Linq,Lambda Expr)

一切順利,帕特里克

Lambda表達式不受調試器的表達式評估器支持...這不足為奇,因為在編譯時,它們用於創建方法(或表達式樹)而不是表達式(在Reflector中將顯示切換到.NET 2來查看)。看他們)。

加上它們當然可以形成閉合,這是另一整個結構層。

為了回答您的問題,這是Visual Studio程序管理器對為什么您不能執行此操作的正式解釋。 簡而言之,因為在VS中實現“確實非常非常困難”。 但是該功能目前正在開發中(2014年8月更新)。

調試時允許評估lambda表達式

在那里就添加投票!

如果仍然需要使用Visual Studio 2013,則實際上也可以使用包管理器控制台窗口在立即窗口中編寫循環或lambda表達式。 就我而言,我在函數頂部添加了一個列表:

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 \\ nThen語句已終止。“} 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 }

這個想法的作者可以在這里找到

在VS 2015中,您現在就可以這樣做,這是他們添加的新功能之一。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM