Tôi gần đây đã bit bởi một điều kỳ lạ với biểu thức lambda và chụp biến. Mã này là một ứng dụng WPF/MVVM sử dụng .NET 4.5 (VS2012). Tôi đã sử dụng nhà thầu khác nhau của viewmodel của tôi để thiết lập các cuộc gọi lại cho một RelayCommand
(lệnh này sau đó sẽ được ràng buộc với một mục trình đơn theo quan điểm của tôi)Tại sao tôi phải nắm bắt lambda đến một biến trường trong cuộc gọi đến nhà xây dựng
Về bản chất, tôi đã có đoạn mã sau:
public class MyViewModel : ViewModelBase
{
public MyViewModel(Action menuCallback)
{
MyCommand = new RelayCommand(menuCallback);
}
public MyViewModel(Func<ViewModelBase> viewModelCreator)
// I also tried calling the other constructor, but the result was the same
// : this(() => SetMainContent(viewModelCreator())
{
Action action =() => SetMainContent(viewModelCreator());
MyCommand = new RelayCommand(action);
}
public ICommand MyCommand { get; private set; }
}
và sau đó tạo các phiên bản ở trên bằng cách sử dụng:
// From some other viewmodel's code:
new MyViewModel(() => new SomeViewModel());
new MyViewModel(() => new SomeOtherViewModel());
Sau đó, chúng được gắn với một Menu WPF - mỗi mục menu có một cá thể MyViewModel làm ngữ cảnh dữ liệu. Điều kỳ lạ là các menu chỉ hoạt động một lần. Bất kể mục nào tôi đã thử, nó sẽ gọi số Func<ViewModelBase>
thích hợp - nhưng chỉ một lần. Nếu tôi cố gắng chọn một mục menu khác hoặc thậm chí cùng một mục một lần nữa, nó chỉ đơn giản là không hoạt động. Không có gì được gọi và không có đầu ra trong đầu ra gỡ lỗi VS về bất kỳ lỗi nào.
Tôi nhận thức được vấn đề với những ảnh chụp biến trong vòng, vì vậy tôi đã đoán rằng vấn đề này có liên quan để thay đổi VM của tôi để:
public class MyViewModel : ViewModelBase
{
public MyViewModel(Action buttonCallback)
{
MyCommand = new RelayCommand(buttonCallback);
}
private Func<ViewModelBase> _creator;
public MyViewModel(Func<ViewModelBase> viewModelCreator)
{
// Store the Func<> to a field and use that in the Action lambda
_creator = viewModelCreator;
var action =() => SetMainContent(_creator());
MyCommand = new RelayCommand(action);
}
public ICommand MyCommand { get; private set; }
}
và gọi nó theo cùng một cách. Bây giờ tất cả mọi thứ hoạt động như nó cần.
Chỉ cần cho vui, tôi cũng làm việc xung quanh toàn bộ Func<ViewModelBase>
constructor bằng cách tạo ra các Action
bên ngoài thích hợp của MyViewModel
constructor:
// This code also works, even without the _creator field in MyViewModel
new MyViewModel(() => SetMainContent(new SomeViewModel()));
new MyViewModel(() => SetMainContent(new SomeOtherViewModel()));
Vì vậy, tôi cố gắng làm cho nó làm việc, nhưng tôi vẫn tò mò tại sao nó hoạt động như thế này Tại sao trình biên dịch không đúng cách chụp Func<ViewModelBase>
trong hàm tạo?
Bạn đã xem xét sự khác biệt trong IL được tạo cho hai cách tiếp cận? Nó có thể đưa ra một số gợi ý. – nicodemus13
Nếu 'SetMainContent' nằm trong lớp' ViewModelBase', bạn gọi nó như thế nào trong lambda trong ví dụ mã cuối cùng của bạn hoạt động? – Pat
SetMainContent sử dụng các thông báo (từ MVVMLight) để gửi cá thể viewmodel tới viewmodel của cửa sổ chính. Sau đó nó sẽ gán nó cho một thuộc tính Nội dung được hiển thị trong giao diện người dùng. Tôi sẽ cố gắng đưa ra một ví dụ mã hoàn chỉnh hơn, thể hiện vấn đề này –