Visual Studio debugging alat "quick watch" dan ekspresi lambda

96

Mengapa saya tidak dapat menggunakan ekspresi lambda saat melakukan debug di jendela "Jam tangan cepat"?

UPD: lihat juga

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

lak-b
sumber
5
Ini telah selesai dan tersedia dalam pratinjau VS 2015. visualstudio.uservoice.com/forums/121579-visual-studio/…
Francisco d'Anconia
saya mencoba contoh yang sangat sederhana yang diberikan pada MSDN untuk ekspresi lambda tetapi tidak berhasil. saya memiliki edisi VS 2015 enterprise
Adeem
2
@ Franciscod'Anconia untuk mengaktifkan dukungan lambda dalam debug, "Gunakan Mode Kompatibilitas Terkelola" harus dicentang ( stackoverflow.com/a/36559817/818321 ) Akibatnya, Anda tidak akan dapat menggunakan titik putus bersyarat: blogs.msdn .microsoft.com / devops / 2013/10/16 /… dan stackoverflow.com/a/35983978/818321
Nik

Jawaban:

64

Ekspresi lambda, seperti metode anonim, sebenarnya adalah makhluk yang sangat kompleks. Bahkan jika kita mengesampingkan Expression(.NET 3.5), itu masih menyisakan banyak kompleksitas, paling tidak variabel yang ditangkap, yang secara fundamental menyusun ulang kode yang menggunakannya (apa yang Anda anggap sebagai variabel menjadi bidang pada kelas yang dihasilkan kompiler) , dengan sedikit asap dan cermin.

Karena itu, saya tidak sedikit pun terkejut bahwa Anda tidak dapat menggunakannya dengan santai - ada banyak pekerjaan kompilator (dan pembuatan tipe di belakang layar) yang mendukung keajaiban ini.

Marc Gravell
sumber
91

Tidak, Anda tidak dapat menggunakan ekspresi lambda di jendela jam tangan / lokal / langsung. Seperti yang ditunjukkan Marc, ini sangat kompleks. Saya ingin menyelami topik ini lebih jauh.

Apa yang kebanyakan orang tidak pertimbangkan dengan menjalankan fungsi anonim di debugger adalah bahwa hal itu tidak terjadi dalam vakum. Tindakan mendefinisikan dan menjalankan fungsi anonim mengubah struktur yang mendasari basis kode. Mengubah kode, secara umum, dan khususnya dari jendela langsung, adalah tugas yang sangat sulit.

Perhatikan kode berikut.

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

Kode khusus ini membuat penutupan tunggal untuk menangkap nilai v1. Tangkapan penutupan diperlukan setiap kali fungsi anonim menggunakan variabel yang dideklarasikan di luar cakupannya. Untuk semua maksud dan tujuan v1 tidak lagi ada dalam fungsi ini. Baris terakhir sebenarnya lebih terlihat seperti berikut

var v3 = closure1.v1 + v2;

Jika Contoh fungsi dijalankan di debugger itu akan berhenti di baris Break. Sekarang bayangkan jika pengguna mengetik berikut ini ke dalam jendela arloji

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

Untuk menjalankan ini dengan benar, debugger (atau lebih tepat EE) perlu membuat penutupan untuk variabel v2. Ini sulit tetapi bukan tidak mungkin untuk dilakukan.

Apa yang benar-benar membuat ini menjadi pekerjaan yang sulit bagi EE adalah baris terakhir itu. Bagaimana seharusnya baris itu sekarang dieksekusi? Untuk semua maksud dan tujuan, fungsi anonim menghapus variabel v2 dan menggantinya dengan closure2.v2. Jadi baris terakhir kode sekarang benar-benar perlu dibaca

var v3 = closure1.v1 + closure2.v2;

Namun untuk benar-benar mendapatkan efek ini dalam kode, EE harus mengubah baris terakhir kode yang sebenarnya merupakan tindakan ENC. Meskipun contoh khusus ini dimungkinkan, sebagian besar skenario tidak.

Yang lebih buruk lagi adalah mengeksekusi ekspresi lambda itu seharusnya tidak membuat penutupan baru. Ini seharusnya benar-benar menambahkan data ke penutupan asli. Pada titik ini Anda langsung menuju ke batasan ENC.

Contoh kecil saya sayangnya hanya menggores permukaan dari masalah yang kita hadapi. Saya terus mengatakan saya akan menulis posting blog lengkap tentang hal ini dan semoga saya punya waktu akhir pekan ini.

JaredPar
sumber
41
Merengek, merengek, menerima biasa-biasa saja, merengek, merengek. Debugger adalah inti dari IDE, dan Anda telah merusaknya! Lambda di jendela arloji tidak perlu menangkap apa pun. Seperti kode jam tangan lainnya, kode tersebut hanya masuk akal pada bingkai tumpukan tertentu. (Atau jika Anda menangkap variabel, pindah ke fungsi lain dengan nama variabel yang sama ... dan apa?) Debugger dimaksudkan untuk meretas kompiler. Buat itu bekerja!
Aleksandr Dubinsky
2
Mengapa mereka sederhana tidak mengizinkan variabel yang diambil pada lambda di jendela jam. Sederhana dan akan memungkinkan banyak skenario debug di mana lambda hanya digunakan dalam kode yang benar-benar berfungsi.
Luiz Felipe
@LuizFelipe bahkan masih dalam pengambilan besar - besaran . Ini membutuhkan EE untuk benar-benar menghasilkan badan fungsi penuh untuk panggilan balik (hingga IL). EE tidak melakukan hal semacam ini hari ini, melainkan penerjemah.
JaredPar
1
@JaredPar dapatkah Anda berbagi posting blog yang sedang dibicarakan marc
Ehsan Sajjad
49

Anda tidak dapat menggunakan ekspresi lambda di jendela Immediate atau Watch.

Namun Anda dapat menggunakan ekspresi System.Linq.Dynamic , yang berbentuk .Where ("Id = @ 0", 2) - tidak memiliki berbagai metode lengkap yang tersedia dalam Linq standar, dan tidak memiliki kekuatan ekspresi lambda, tapi tetap saja, itu lebih baik daripada tidak sama sekali!

stusherwin
sumber
2
Yah ... sementara yang lain menjelaskan meskipun itu tidak mungkin, yang satu ini setidaknya memberi kita solusi yang memungkinkan. +1
Nullius
1
Hanya untuk memperjelas, Anda "Impor System.Linq.Dynamic" dan kemudian di jendela debug Anda menulis '"Di mana (sesuatu.AsQueryable," property> xyz ", nothing)'
smirkingman
Ini bagus. Meskipun Anda tidak mendapatkan rangkaian lengkap dari metode Ekstensi Linq, misalnya tidak ada .Any(string predicate), Anda dapat meletakkan sesuatu seperti: .Where("Id>2").Any()di Jendela Tonton, atau Sematkan ke Sumber. Itu bagus!
Pelindung satu
22

Masa depan telah datang!

Dukungan untuk debugging ekspresi lambda telah ditambahkan ke Visual Studio 2015 ( Pratinjau pada saat penulisan).

Expression Evaluator harus ditulis ulang, begitu banyak fitur yang hilang: ASP.NET debugging jarak jauh, mendeklarasikan variabel di jendela Immediate, memeriksa variabel dinamis, dll. Juga ekspresi lambda yang memerlukan panggilan ke fungsi asli saat ini tidak didukung.

Athari
sumber
2

Ekspresi lambda tidak didukung oleh penilai ekspresi debugger ... yang tidak mengherankan karena pada waktu kompilasi mereka digunakan untuk membuat metode (atau Pohon Ekspresi) daripada ekspresi (lihat di Reflektor dengan tampilan yang dialihkan ke .NET 2 ke Lihat mereka).

Ditambah tentu saja mereka bisa membentuk penutup, seluruh lapisan struktur lainnya.

Richard
sumber
Nah, mereka mungkin menciptakan metode; mereka mungkin membuat Expressionpohon - tergantung pada konteksnya.
Marc Gravell
1

Di VS 2015 Anda dapat melakukannya sekarang, ini adalah salah satu fitur baru yang mereka tambahkan.

loneshark99
sumber
1

Jika Anda masih perlu menggunakan Visual Studio 2013, Anda sebenarnya dapat menulis loop, atau ekspresi lambda di jendela langsung menggunakan juga jendela konsol manajer paket. Dalam kasus saya, saya menambahkan daftar di bagian atas fungsi:

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

Dimana GetAll()fungsi saya adalah:

private DbSet<T> _dbSet;

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

    return list;
}

Di sini saya terus mendapatkan kesalahan berikut, jadi saya ingin mencetak semua item di berbagai repositori:

InnerException {"Pernyataan DELETE berkonflik dengan batasan REFERENCE \" FK_dbo.Department_dbo.RoleHierarchy_OranizationalRoleId \ ". Konflik terjadi di database \" CC_Portal_SchoolObjectModel \ ", table \" dbo.Department \ ", kolom 'OranizationalRoleId \" pernyataan telah dihentikan. "} System.Exception {System.Data.SqlClient.SqlException}

Kemudian, saya mencari tahu berapa banyak record yang ada di repositori departemen dengan menjalankan ini di jendela langsung:

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

Yang mengembalikan 243.

Jadi, jika Anda menjalankan perintah berikut di konsol pengelola paket, semua item akan dicetak:

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

Penulis gagasan dapat ditemukan di sini

pengguna8128167
sumber
1

Untuk menjawab pertanyaan Anda, berikut ini penjelasan resmi Visual Studio Program Manager tentang mengapa Anda tidak dapat melakukan ini. Singkatnya, karena "sangat, sangat sulit" untuk diterapkan di VS. Tetapi fitur tersebut sedang dalam proses (diperbarui pada Agustus 2014).

Izinkan evaluasi ekspresi lambda saat melakukan debug

Tambahkan suara Anda saat Anda di sana!

Francisco d'Anconia
sumber