Pregunta

Creo que esta es la pregunta, de todos modos.Estoy usando un RelayCommand, que decora una ICommand con dos delegados.Uno es el Predicado de la _canExecute y el otro es de Acción para la _execute método.

---Antecedentes de la motivación --

La motivación tiene que ver con la unidad de pruebas ViewModels para un WPF presentación.Un patrón frecuente es que tengo un ViewModel que tiene una ObservableCollection, y quiero una unidad de prueba para demostrar los datos de la colección es de lo que yo esperaría dado algunos datos de origen (que también debe convertirse en una colección de Viewmodel).Aunque los datos en ambas colecciones se ve igual en el depurador, se parece a la prueba falla debido a una igualdad fracaso en la Perspective del RelayCommand.He aquí un ejemplo de la falta de unidad de la prueba:

[Test]
    public void Creation_ProjectActivities_MatchFacade()
    {
        var all = (from activity in _facade.ProjectActivities
                   orderby activity.BusinessId
                   select new ActivityViewModel(activity, _facade.SubjectTimeSheet)).ToList();

        var models = new ObservableCollection<ActivityViewModel>(all);
        CollectionAssert.AreEqual(_vm.ProjectActivities, models);
    }

--- La vuelta a la delegada de igualdad ----

Aquí está el código para RelayCommand - es básicamente un directo rip-off de Josh Smith idea, con una aplicación para la igualdad que he añadido en un intento de resolver este problema:

public class RelayCommand : ICommand, IRelayCommand
{
    readonly Action<object> _execute;
    readonly Predicate<object> _canExecute;

    /// <summary>Creates a new command that can always execute.</summary>
    public RelayCommand(Action<object> execute) : this(execute, null) { }

    /// <summary>Creates a new command which executes depending on the logic in the passed predicate.</summary>
    public RelayCommand(Action<object> execute, Predicate<object> canExecute) {
        Check.RequireNotNull<Predicate<object>>(execute, "execute");

        _execute = execute;
        _canExecute = canExecute;
    }

    [DebuggerStepThrough]
    public bool CanExecute(object parameter) { return _canExecute == null ? true : _canExecute(parameter); }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter) { _execute(parameter); }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != typeof(RelayCommand)) return false;
        return Equals((RelayCommand)obj);
    }

    public bool Equals(RelayCommand other)
    {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(other._execute, _execute) && Equals(other._canExecute, _canExecute);
    }

    public override int GetHashCode()
    {
        unchecked
        {
            return ((_execute != null ? _execute.GetHashCode() : 0) * 397) ^ (_canExecute != null ? _canExecute.GetHashCode() : 0);
        }
    }

}

En una unidad de prueba, donde he efectivamente conjunto de la _execute delegado para el mismo método (_canExecute es nulo en ambos casos), la prueba de la unidad falla en esta línea:

return Equals(other._execute, _execute) && Equals(other._canExecute, _canExecute)

De salida del depurador:

?_execute
{Method = {Void <get_CloseCommand>b__0(System.Object)}}
base {System.MulticastDelegate}: {Method = {Void CloseCommand>b__0(System.Object)}}

?other._execute
{Method = {Void <get_CloseCommand>b__0(System.Object)}} 
base {System.MulticastDelegate}: {Method = {Void CloseCommand>b__0(System.Object)}}

¿Alguien puede explicar lo que me falta y lo que es la corrección?

---- EDITADO OBSERVACIONES ----

Como Mehrdad señalado, el get_CloseCommand de la sesión de depuración se ve un poco raro al principio.De verdad que es sólo una propiedad, pero sí aumenta el punto de por qué la igualdad del delegado es problemático si tengo que hacer trucos para hacer que funcione.

Algunos de los punto de MVVM es exponer lo que pueda ser útil en una presentación como las propiedades, así que usted puede utilizar WPF unión.La clase estaba probando tiene un WorkspaceViewModel en su jerarquía, que es sólo un ViewModel que ya tiene cerca de un comando de la propiedad.Aquí está el código:

public abstract class WorkspaceViewModel :ViewModelBase {

    /// <summary>Returns the command that, when invoked, attempts to remove this workspace from the user interface.</summary>
    public ICommand CloseCommand
    {
        get
        {
            if (_closeCommand == null)
                _closeCommand = new RelayCommand(param => OnRequestClose());

            return _closeCommand;
        }
    }
    RelayCommand _closeCommand;

    /// <summary>Raised when this workspace should be removed from the UI.</summary>
    public event EventHandler RequestClose;

    void OnRequestClose()
    {
        var handler = RequestClose;
        if (handler != null)
            handler(this, EventArgs.Empty);
    }

    public bool Equals(WorkspaceViewModel other) {
        if (ReferenceEquals(null, other)) return false;
        if (ReferenceEquals(this, other)) return true;
        return Equals(other._closeCommand, _closeCommand) && base.Equals(other);
    }

    public override int GetHashCode() {
        unchecked {
            {
                return (base.GetHashCode() * 397) ^ (_closeCommand != null ? _closeCommand.GetHashCode() : 0);
            }
        }
    }
}

Se puede ver que el comando cerrar es un RelayCommand, y que me monkeyed con iguales para hacer la prueba de la unidad de trabajo.

@Merhdad Aquí está la prueba de la unidad que sólo funciona cuando uso el Timador del delegado.Método de la comparación de igualdad.

[TestFixture] clase pública WorkspaceViewModelTests { privado WorkspaceViewModel vm1;privado WorkspaceViewModel vm2;

    private class TestableModel : WorkspaceViewModel
    {

    }

    [SetUp]
    public void SetUp() {
        vm1 = new TestableModel();
        vm1.RequestClose += OnWhatever;
        vm2 = new TestableModel();
        vm2.RequestClose += OnWhatever;
    }

    private void OnWhatever(object sender, EventArgs e) { throw new NotImplementedException(); }


    [Test]
    public void Equality() {
        Assert.That(vm1.CloseCommand.Equals(vm2.CloseCommand));
        Assert.That(vm1.Equals(vm2));
    }


}

----- EDICIONES MÁS RECIENTES PARA UTILIZAR MERHDAD"LA IDEA DE

depurador de salida de la ?valueOfThisObject {Justo.Wpf.ViewModel.RelayCommand} base {SharpArch.Núcleo.DomainModel.ValueObject}:{Justo.Wpf.ViewModel.RelayCommand} _canExecute:null _execute:{Method = {Void _executeClose(del Sistema.Objeto)}}

?valueToCompareTo
{Smack.Wpf.ViewModel.RelayCommand}
base {SharpArch.Core.DomainModel.ValueObject}: {Smack.Wpf.ViewModel.RelayCommand}
_canExecute: null
_execute: {Method = {Void _executeClose(System.Object)}}

?valueOfThisObject.Equals(valueToCompareTo)
false

Este es el resultado después de cambiar el código para:

    public ICommand CloseCommand
    {
        get
        {
            if (_closeCommand == null)
                _closeCommand = new RelayCommand(_executeClose);

            return _closeCommand;
        }
    }
    RelayCommand _closeCommand;

    void _executeClose(object param) {
        OnRequestClose();
    }
¿Fue útil?

Solución

Se va a crear el delegado de funciones anónimas o algo?Estos son los exactos, delegado de igualdad de las reglas de acuerdo con C# especificación (§7.9.8):

Delegado operadores de igualdad

Dos instancias de delegado se consideran iguales de la siguiente manera:Si cualquiera de las instancias de delegado es null, son iguales si y sólo si ambas son null.
Si los delegados tienen diferente tipo en tiempo de ejecución ellos nunca son iguales.Si las dos instancias de delegado tiene una lista de invocación (§15.1), esos casos son iguales si y sólo si su invocación listas de la misma longitud, y cada entrada en la lista de invocación es igual (como se define a continuación) a la entrada correspondiente, en fin, la lista de invocación.Las siguientes normas rigen la igualdad de invocación de las entradas de la lista:
Si dos invocación de la lista de entradas de ambos se refieren a la misma static método las entradas son iguales.
Si dos invocación de la lista de entradas de ambos se refieren a la misma nostatic método en el mismo objeto de destino (como se define por la referencia operadores de igualdad), a continuación, las entradas son iguales.
La invocación de las entradas de la lista producido a partir de la evaluación de semánticamente idénticas anónimo-la función de las expresiones con el mismo (posiblemente vacía) conjunto de capturó variable externa instancias son permitido (pero no necesario) a ser igual.

Así que, en tu caso, es posible que el delegado de los casos están refiriendo a el mismo método en dos objetos diferentes, o hacer referencia a dos métodos anónimos.


ACTUALIZACIÓN: De hecho, el problema es que no está pasando por el mismo método de referencia cuando usted está llamando new RelayCommand(param => OnCloseCommand()).Después de todo, la expresión lambda especificado aquí es en realidad un método anónimo (que no pasa de un método de referencia para OnCloseCommand;se pasa una referencia a un método anónimo que toma un único parámetro y llamadas OnCloseCommand).Como se mencionó en la última línea de la especificación de la cita anterior, no es necesario que la comparación de los dos delegados de retorno true.

Nota Al Margen: El captador de la CloseCommand la propiedad sería llamado simplemente get_CloseCommand y no <get_CloseCommand>b__0.Este es el compilador genera el nombre de método para el método anónimo dentro de get_CloseCommand (método de la CloseCommand captador).Esto corrobora el punto que he mencionado anteriormente.

Otros consejos

No sé nada ahora sobre otras líneas, pero ¿y si

CollectionAssert.AreEqual(_vm.ProjectActivities, models);

falla solo porque se utiliza ReferenceEquality?

Ha anulado la comparación para RelayCommand pero no para ObservableCollection.

Y parece que en el caso de Delegados, también se usa la igualdad de referencia.

Intente comparar por Delegate.Method en su lugar.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top