? Me ha picado algo extraño con la expresión lambda y las capturas variables. El código era una aplicación WPF/MVVM que usa .NET 4.5 (VS2012). Yo estaba usando diferentes constructores de mi modelo de vista de configurar la devolución de llamada para un RelayCommand
(este comando sería entonces estar unido a un elemento de menú en mi opinión)¿Por qué tengo que capturar la lambda a una variable de campo en la llamada al constructor
En esencia, tenía el siguiente código:
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; }
}
y luego creó instancias del anterior usando:
// From some other viewmodel's code:
new MyViewModel(() => new SomeViewModel());
new MyViewModel(() => new SomeOtherViewModel());
Estos fueron obligados a un menú de WPF - cada elemento del menú tenía una instancia MyViewModel como su contexto de datos. Lo extraño fue que los menús solo funcionaron una vez. Independientemente de cuál de los artículos probé, llamaría al Func<ViewModelBase>
apropiado, pero solo una vez. Si traté de seleccionar otra opción del menú o incluso el mismo artículo nuevamente, simplemente no funcionó. No se ha llamado nada y no hay salida en la salida de depuración de VS sobre ningún error.
Soy consciente de los problemas con las capturas de variables en bucles, así que hice una conjetura que esta cuestión se relaciona de modo cambiado de máquina virtual para:
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; }
}
y lo llamó de la misma manera. Ahora todo funciona como debería.
Sólo por diversión, también trabajé alrededor de toda la Func<ViewModelBase>
constructor mediante la creación de la adecuada Action
MyViewModel
fuera del constructor:
// This code also works, even without the _creator field in MyViewModel
new MyViewModel(() => SetMainContent(new SomeViewModel()));
new MyViewModel(() => SetMainContent(new SomeOtherViewModel()));
Así que me las arreglé para conseguir que funcione, pero todavía estoy curioso por qué funciona así ¿Por qué el compilador no captura correctamente el Func<ViewModelBase>
en el constructor?
¿Has mirado las diferencias en el IL generado para los dos enfoques? Puede dar algunas pistas. – nicodemus13
Si 'SetMainContent' está en la clase' ViewModelBase', ¿cómo lo está llamando en la lambda en el último ejemplo de código que funciona? – Pat
SetMainContent usa mensajes (desde MVVMLight) para enviar la instancia de viewmodel al modelo de vista de la ventana principal. Luego lo asignará a una propiedad de contenido que se procesa en la interfaz de usuario. Trataré de encontrar un ejemplo de código más completo que muestre este problema –