2012-10-04 9 views
10

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?

+1

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

+0

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

+0

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 –

Trả lời

2

Tôi đoán các mã sau đây cũng sẽ làm việc

public MyViewModel(Func<ViewModelBase> viewModelCreator) 
{ 
    var action =() => { creator = viewModelCreator; SetMainContent(creator()); }; 
    MyCommand = new RelayCommand(action); 
} 

Nếu vậy, thì lý do nó không được làm việc theo cách đầu tiên là bạn không thực sự đóng xung quanh biến viewModelCreator, bạn' đóng lại xung quanh kết quả của việc gọi nó.

Tôi vẫn đang phát xung quanh với mã trong LINQPad, nhưng nó không thực sự có vẻ như tôi đang nhận được cùng một vấn đề mà bạn đang có. Có lẽ nó là một cái gì đó cụ thể để RelayCommand. Bạn có thể đăng thêm mã của mình không?

+0

Cảm ơn câu trả lời. RelayCommand là từ MVVM ánh sáng: http://bit.ly/QRLCES –

+1

Hmm .. Cố gắng để tái sản xuất nó trên một máy tính khác (sử dụng VS2010) bằng cách sử dụng mã từ câu hỏi của riêng tôi nhưng tôi không nhìn thấy nó .. Tôi cần phải đi trở lại mã ban đầu của tôi và xem sự khác biệt là gì (mã câu hỏi của tôi được đơn giản hóa - và có thể là tôi đã đơn giản hóa quá nhiều và xóa sự cố trong quá trình) –