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/…
Francisco d'Anconia


ฉันลองใช้ตัวอย่างง่ายๆที่ให้ไว้ใน MSDN สำหรับแลมบ์ดานิพจน์ แต่ไม่ได้ผล ฉันมี VS 2015 Enterprise Edition
Adeem

2
@ Franciscod'Anconia เพื่อเปิดใช้งานการสนับสนุน lambda ในการดีบัก "Use Managed Compatibility Mode" จะต้องถูกกาเครื่องหมาย( 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

ไม่คุณไม่สามารถใช้นิพจน์แลมบ์ดาในหน้าต่างนาฬิกา / ชาวบ้าน / ทันที ดังที่ 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 และแทนที่ด้วย closed2.v2 ตอนนี้บรรทัดสุดท้ายของโค้ดจำเป็นต้องอ่านจริงๆ

var v3 = closure1.v1 + closure2.v2;

แต่เพื่อให้ได้เอฟเฟกต์นี้ในโค้ดต้องใช้ EE เพื่อเปลี่ยนบรรทัดสุดท้ายของโค้ดซึ่งเป็นแอ็คชัน ENC แม้ว่าตัวอย่างเฉพาะนี้จะเป็นไปได้ แต่ส่วนที่ดีของสถานการณ์ไม่ได้

สิ่งที่แย่ไปกว่านั้นคือการดำเนินการนิพจน์แลมด้าไม่ควรสร้างการปิดใหม่ ที่จริงแล้วควรต่อท้ายข้อมูลการปิดเดิม ณ จุดนี้คุณจะเข้าสู่ข้อ จำกัด ENC โดยตรง

ตัวอย่างเล็ก ๆ ของฉันน่าเสียดายที่ทำให้เกิดรอยขีดข่วนบนพื้นผิวของปัญหาที่เราพบเท่านั้น ฉันพูดอยู่เสมอว่าจะเขียนบทความในบล็อกเกี่ยวกับเรื่องนี้และหวังว่าฉันจะมีเวลาในสุดสัปดาห์นี้


41
สะอื้นสะอื้นยอมรับคนธรรมดาสะอื้นสะอื้น ดีบักเกอร์คือหัวใจของ IDE และคุณทำมันพัง! Lambdas ในหน้าต่างนาฬิกาไม่จำเป็นต้องจับภาพอะไรเลย เช่นเดียวกับรหัสนาฬิกาอื่น ๆ พวกเขามีความหมายเฉพาะในสแต็กเฟรมเท่านั้น (หรือมิฉะนั้นคุณจับตัวแปรย้ายไปฟังก์ชั่นอื่นที่มีชื่อตัวแปรเดียวกัน ... และอะไร) ดีบักเกอร์มีไว้เพื่อแฮ็กรอบ ๆ คอมไพเลอร์ ทำให้ได้ผล!
Aleksandr Dubinsky

2
ทำไมพวกมันง่ายไม่อนุญาตให้จับตัวแปรบน lambdas บนหน้าต่างนาฬิกา ง่ายและอนุญาตให้มีสถานการณ์การดีบักจำนวนมากที่ lambdas ใช้ในโค้ดที่ใช้งานได้จริง
Luiz Felipe

@LuizFelipe แม้กระทั่งว่ายังคงมีขนาดใหญ่ภายใต้การซัก ต้องใช้ EE เพื่อสร้างเนื้อความเต็มสำหรับการโทรกลับ (ตลอดจนถึง IL) วันนี้ EE ไม่ได้ทำอะไรแบบนี้ แต่เป็นล่ามแทน
JaredPar

1
@JaredPar คุณสามารถแชร์บล็อกโพสต์ที่มาร์คพูดถึง
Ehsan Sajjad

49

คุณไม่สามารถใช้นิพจน์แลมบ์ดาในหน้าต่างทันทีหรือดู

อย่างไรก็ตามคุณสามารถใช้นิพจน์ System.Linq.Dynamicซึ่งอยู่ในรูปแบบที่ใด ("Id = @ 0", 2) - มันไม่มีวิธีการทั้งหมดที่มีอยู่ใน Linq มาตรฐานและไม่มีแบบเต็ม พลังแห่งการแสดงออกของแลมบ์ดา แต่ถึงกระนั้นก็ดีกว่าไม่มีอะไรเลย!


2
ดี ... ในขณะที่คนอื่นอธิบายในขณะที่มันเป็นไปไม่ได้อย่างน้อยอันนี้ก็ให้ทางออกที่เป็นไปได้แก่เรา +1
Nullius

1
เพื่อชี้แจงคุณ "นำเข้า System.Linq.Dynamic" จากนั้นในหน้าต่างดีบักคุณเขียนว่า '"Where (something.AQueryable," property> xyz ", nothing)'
smirkingman

นี่มันเยี่ยมมาก แม้ว่าคุณจะไม่ได้รับวิธี Linq Extension แบบครบวงจร แต่ก็ไม่มี.Any(string predicate)คุณสามารถใส่สิ่งต่างๆเช่น: .Where("Id>2").Any()ในหน้าต่างนาฬิกาหรือปักหมุดที่แหล่งที่มา มันยอดมาก!
ผู้พิทักษ์

22

อนาคตมาแล้ว!

เพิ่มการสนับสนุนสำหรับการดีบักนิพจน์แลมบ์ดาในVisual Studio 2015 ( ดูตัวอย่างในขณะที่เขียน)

ต้องมีการเขียน Expression Evaluator คุณสมบัติหลายอย่างจึงขาดหายไป: การดีบัก ASP.NET ระยะไกลการประกาศตัวแปรในหน้าต่างทันทีการตรวจสอบตัวแปรไดนามิกเป็นต้นนอกจากนี้ยังไม่รองรับนิพจน์แลมบ์ดาที่ต้องเรียกใช้ฟังก์ชันเนทีฟ


น่าดู เย็น...!
Rahul Nikate


5

สิ่งนี้อาจช่วยได้: Extended Immediate Window for Visual Studio (ใช้ Linq, Lambda Expr ในการดีบัก)

ที่ดีที่สุดแพทริค


5
โปรดทราบว่าแม้ว่าลิงก์แรกจะดูดี แต่ก็เป็นแบบอัลฟ่าและไม่น่าจะหลุดออกมาเลย (อัปเดตครั้งล่าสุดในปี 2008)
John Salvatier

2

เครื่องมือประเมินนิพจน์ของดีบักเกอร์ไม่รองรับนิพจน์ ... ซึ่งแทบจะไม่น่าแปลกใจเลยเนื่องจากในเวลาคอมไพล์พวกเขาถูกใช้เพื่อสร้างเมธอด (หรือ Expression Trees) แทนที่จะเป็นนิพจน์ (ดูใน Reflector พร้อมกับการแสดงผลที่เปลี่ยนเป็น. NET 2 เป็น ดูพวกเขา)

นอกจากนี้ยังสามารถก่อตัวปิดซึ่งเป็นโครงสร้างอีกชั้นหนึ่ง


พวกเขาอาจสร้างวิธีการ พวกเขาอาจสร้างExpressionต้นไม้ขึ้นอยู่กับบริบท
Marc Gravell

1

ใน VS 2015 คุณสามารถทำได้แล้วนี่เป็นหนึ่งในคุณสมบัติใหม่ที่พวกเขาเพิ่มเข้ามา


1

หากคุณยังต้องใช้ Visual Studio 2013 คุณสามารถเขียน loop หรือ lambda expression ในหน้าต่างทันทีโดยใช้หน้าต่างคอนโซลตัวจัดการแพ็คเกจ ในกรณีของฉันฉันได้เพิ่มรายการที่ด้านบนสุดของฟังก์ชัน:

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 ขัดแย้งกับข้อ จำกัด การอ้างอิง \" FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \ "ความขัดแย้งเกิดขึ้นในฐานข้อมูล \" CC_Portal_SchoolObjectModel \ ", ตาราง \" dbo.Department \ ", คอลัมน์ 'OranizationalRoleId' คำสั่งถูกยกเลิก "} 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 }

ผู้เขียนสำหรับแนวคิดนี้สามารถพบได้ที่นี่


1

หากต้องการตอบคำถามของคุณนี่คือคำอธิบายอย่างเป็นทางการของ Visual Studio Program Manager เกี่ยวกับสาเหตุที่คุณไม่สามารถทำได้ ในระยะสั้นเพราะ "มันยากจริงๆ" ในการนำไปใช้ใน VS. แต่ฟีเจอร์ดังกล่าวกำลังอยู่ในระหว่างดำเนินการ (อัปเดตเมื่อ ส.ค. 2014)

อนุญาตให้ประเมินนิพจน์แลมบ์ดาขณะดีบัก

เพิ่มคะแนนของคุณในขณะที่คุณอยู่ที่นั่น!

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.