Frage

Ich denke, das ist die Frage, sowieso. Ich verwende einen RelayCommand, die eine ICommand mit zwei Delegierten verziert. Eine davon ist Prädikat für die _canExecute und die anderen Aktion für die _EXECUTE Methode.

--- Hintergrund Motivation -

Die Motivation hat mit Unit-Tests für ein Viewmodel WPF Präsentation zu tun. Ein häufiges Muster ist, dass ich ein Ansichtsmodell haben, das eine ObservableCollection hat, und ich möchte ein Gerät zu testen, die Daten in dieser Sammlung zu beweisen, ist das, was ich ein paar Quelldaten gegeben erwarten (die auch in eine Sammlung von Viewmodels umgewandelt werden muss). Auch wenn die Daten in beiden Sammlungen gleich im Debugger sieht, sieht es aus wie der Test aufgrund einer Gleichheit Versagen auf der RelayCommand des Ansichtsmodell versagt. Hier ist ein Beispiel für die fehlerhafte Einheit Test:

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

--- Zurück zu delegieren Gleichheit ----

Hier

ist der Code für die RelayCommand - es ist im Grunde ein direkter rip-off von Josh Smith Idee, mit einer Implementierung für die Gleichstellung, die ich in einem Versuch hinzugefügt, dieses Problem zu lösen:

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

}

In einem Unit-Test, wo ich effektiv die _EXECUTE Delegierten das gleiche Verfahren festgelegt habe (_canExecute ist null in beiden Fällen), der Unit-Test in dieser Zeile nicht:

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

Debugger Ausgabe:

?_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)}}

Kann mir jemand erklären, was mir fehlt und was die Lösung ist?

---- ---- EDITED Anmerkungen

Wie Mehrdad wies darauf hin, das get_CloseCommand aus der Debug-Sitzung sieht ein bisschen auf dem ersten seltsam. Es ist wirklich nur eine Eigenschaft, aber es tut erhöhen den Punkt, warum die Gleichheit des Delegierten ist problematisch, wenn ich Tricks tun müssen, damit es funktioniert.

Einige der Punkt von MVVM ist zu entlarven, was in einer Präsentation als Eigenschaften nützlich sein könnten, so können Sie WPF Bindung verwenden. Die besondere Klasse war ich Test eine WorkspaceViewModel hat darin Hierarchie ist, die nur ein Ansichtsmodell, das bereits eine enge Befehl Eigenschaft hat. Hier ist der Code:

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

Sie sehen, dass der Schließbefehl ein RelayCommand ist, und dass ich monkeyed mit equals das Gerät Testarbeit zu machen.

@Merhdad Hier ist das Gerät zu testen, die nur funktioniert, wenn ich Schwindler delegate.Method im Gleichheitsvergleich verwendet werden.

[TestFixture]     public class WorkspaceViewModelTests     {         Private WorkspaceViewModel vm1;         Privat 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));
    }


}

----- AKTUELLE EDITS VERWENDUNG MERHDAD "S IDEA

Debugger heraus gesetzt     ? valueOfThisObject     {Smack.Wpf.ViewModel.RelayCommand}     Basis {SharpArch.Core.DomainModel.ValueObject}: {Smack.Wpf.ViewModel.RelayCommand}     _canExecute: null     _EXECUTE: {Method = {void _executeClose (System.Object)}}

?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

Dies ist das Ergebnis nach dem Code zu ändern:

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

            return _closeCommand;
        }
    }
    RelayCommand _closeCommand;

    void _executeClose(object param) {
        OnRequestClose();
    }
War es hilfreich?

Lösung

Erstellen Sie die Delegaten aus anonymen Funktionen oder so etwas? Dies sind die genauen Delegierten Gleichheit Regeln nach C # Spezifikation (§7.9.8):

  

Delegieren Gleichheitsoperator

     

Zwei Delegierten Instanzen gelten als gleich, wie folgt:   Wenn eine der Delegate-Instanzen null ist, sind sie gleich, wenn und nur wenn beide null .
  Wenn die Delegierten unterschiedlichen Laufzeittyp haben sie sind nie gleich .   Wenn beide der Delegierten Fälle eine Aufrufliste haben (§ 15.1), diese Instanzen gleich sind, wenn und nur wenn ihre Aufruflisten gleich lang sind, und jeder Eintrag in einem der Aufrufliste ist gleich (wie unten definiert) auf den entsprechenden Eintrag, um, in der Aufrufliste des anderen.   Die folgenden Regeln gelten für die Gleichheit der Aufrufliste Einträge:
  Wenn zwei Aufrufliste Einträge sowohl beziehen sich auf die gleiche static Methode dann die Einträge sind gleich.
  Wenn zwei Aufrufliste Einträge sowohl beziehen sich auf die gleichen nicht-static Methode auf der gleiche Zielobjekt (wie durch die Bezugsgleichheitsoperatoren definiert ist), dann werden die Einträge gleich sind.
  Invocation Listeneinträge aus der Auswertung von semantisch identisch hergestellt anonymous-Funktion-Ausdrücke mit der gleichen (möglicherweise leeren) Menge von aufgenommenen äußeren variablen Instanzen ist erlaubt (aber nicht erforderlich ) , um gleich.

Also, in Ihrem Fall ist es möglich, dass die Delegierten Instanzen der gleichen Methode in zwei verschiedene Objekte beziehen, oder sich auf zwei anonyme Methoden.


UPDATE: Ja, das Problem ist, dass Sie nicht die gleiche Methode Referenz sind vorbei, wenn Sie new RelayCommand(param => OnCloseCommand()) anrufen. Immerhin hier angegebene Lambda-Ausdruck ist eigentlich eine anonyme Methode (Sie sind OnCloseCommand kein Verfahren Bezug geleitet wird; Sie einen Verweis auf eine anonyme Methode übergeben, die einen einzigen Parameter und ruft OnCloseCommand). Wie oben in der letzten Zeile der Beschreibung Zitats erwähnt, ist es nicht notwendig, dass der Vergleich dieser beiden Delegierten true zurück.

Side Hinweis: Der Getter der CloseCommand Eigenschaft würde einfach get_CloseCommand und nicht <get_CloseCommand>b__0 aufgerufen werden. Dies ist der Compiler erzeugten Methodennamen für die anonyme Methode innerhalb get_CloseCommand Methode (die CloseCommand Getter). Dieser weitere beweist den Punkt ich oben erwähnt.

Andere Tipps

Ich weiß jetzt nichts über andere Linien, aber was ist, wenn

CollectionAssert.AreEqual(_vm.ProjectActivities, models);

nicht nur weil ReferenceEquality verwendet wird?

Sie haben den Vergleich für RelayCommand außer Kraft gesetzt, aber nicht für ObservableCollection.

Und es sieht aus wie im Falle der Delegierten Bezugs Gleichheit auch verwendet wird.

Versuchen von Delegate.Method statt zu vergleichen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top