문제

어쨌든 이것이 질문이라고 생각합니다. 저는 릴레이 커맨드를 사용하고 있습니다. 하나는 _canexecute에 대한 술어이고 다른 하나는 _execute 방법에 대한 조치입니다.

--- 배경 동기 부여-

동기는 단위 테스트 뷰 모델과 관련이 있습니다. WPF 프레젠테이션. 빈번한 패턴은 관측형 수집이있는 하나의 뷰 모델이 있으며, 해당 컬렉션의 데이터가 일부 소스 데이터 (뷰 모델 모음으로 변환되어야 함)가 기대하는 것임을 증명하기위한 단위 테스트를 원한다는 것입니다. 두 컬렉션의 데이터가 디버거에서 동일하게 보이지만 ViewModel의 릴레이 명령의 평등 실패로 인해 테스트가 실패한 것처럼 보입니다. 다음은 실패한 단위 테스트의 예입니다.

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

--- 평등을 위임하는 것으로 돌아가서 ----

다음은 릴레이 명령의 코드입니다. 기본적으로 Josh Smith의 아이디어를 직접 찢어 버린 것입니다.이 문제를 해결하기 위해 추가 한 평등을 구현했습니다.

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

}

_Execute 대표단을 동일한 방법으로 효과적으로 설정하는 단위 테스트 에서이 라인에서 단위 테스트가 실패합니다.

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

디버거 출력 :

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

누구든지 내가 놓친 것을 설명 할 수 있고 수정 사항이 무엇인지 설명 할 수 있습니까?

---- 편집 된 발언 ----

Mehrdad가 지적했듯이, 디버그 세션의 get_closecommand는 처음에는 조금 이상하게 보입니다. 그것은 단지 재산이 될 뿐이지 만, 그것이 작동하기 위해 트릭을해야한다면 대의원의 평등이 문제가되는 이유에 대한 요점을 제기합니다.

MVVM의 일부는 프레젠테이션에 속성으로 유용 할 수있는 모든 것을 노출시켜 WPF 바인딩을 사용할 수 있습니다. 내가 테스트 한 특정 클래스에는 workspaceviewmodel이 있습니다. 코드는 다음과 같습니다.

공개 초록 클래스 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);
            }
        }
    }
}

Close 명령은 릴레이 명령이며, 단위 테스트를 수행하기 위해 동등하게 멍청한 것을 알 수 있습니다.

@Merhdad 여기에 Trickster의 대표를 사용할 때만 작동하는 단위 테스트입니다.

TestFixture] 공개 클래스 WorkSpaceViewModeltests {private workSpaceViewModel vm1; Private 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));
    }


}

----- Merhdad "의 아이디어를 사용하기위한 최신 편집

debugger out put? valueof thisObject {smack.wpf.viewmodel.relaycommand} base {sharparch.core.domainmodel.valueobject} : {smack.wpf.viewmodel.raycommand} _canexecute : null _execute (null _execute) {void _ executeclose }}

?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

코드를 변경 한 후 결과입니다.

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

            return _closeCommand;
        }
    }
    RelayCommand _closeCommand;

    void _executeClose(object param) {
        OnRequestClose();
    }
도움이 되었습니까?

해결책

익명의 기능이나 무언가로 대의원을 만들고 있습니까? C# 사양 (§7.9.8)에 따른 정확한 위임 평등 규칙입니다.

평등 운영자를 위임합니다

두 명의 대표 인스턴스가 다음과 같은 것으로 간주됩니다. 대의원 중 하나가 null, 둘 다있는 경우에만 동일합니다 null.
대의원이 런타임 유형이 다른 경우 그들은 결코 같지 않습니다. 두 대리인 인스턴스에 호출 목록 (§15.1)이있는 경우, 해당 인스턴스는 호출 목록이 길이가 동일하고 호출 목록의 각 항목이 해당 항목과 동일하다면 동일합니다. 다른 사람의 호출 목록에서. 다음 규칙은 호출 목록 항목의 평등을 관리합니다.
두 개의 호출 목록 항목이 둘 다 같은 것을 참조하십시오 static 방법 그런 다음 항목이 동일합니다.
두 개의 호출 목록 항목이 둘 다 동일한 비를 참조하십시오.static 방법동일한 대상 객체 (참조 평등 연산자에 의해 정의 된 바와 같이) 항목은 동일합니다.
Semantically 동일한 평가에서 생성 된 호출 목록 항목 익명 기능-표현 캡처 된 외부 변수 인스턴스 동일 (아마도 비어있을 가능성이있는) 세트 ~이다 허용 (필요하지 않음) 평등합니다.

따라서 귀하의 경우 대의원 인스턴스가 두 개의 다른 객체에서 동일한 방법을 참조하거나 두 가지 익명 방법을 참조 할 수 있습니다.


업데이트: 실제로 문제는 전화 할 때 동일한 방법 참조를 전달하지 않는다는 것입니다. new RelayCommand(param => OnCloseCommand()). 결국, 여기에 지정된 람다 표현식은 실제로 익명의 방법입니다 (당신은 메소드 참조를 전달하지 않습니다. OnCloseCommand; 단일 매개 변수와 호출을 취하는 익명 방법에 대한 참조를 전달하고 있습니다. OnCloseCommand). 위의 사양 인용문의 마지막 줄에서 언급했듯이 두 대의원을 비교할 필요는 없습니다. true.

사이드 참고 : 의 getter CloseCommand 재산은 간단하게 호출됩니다 get_CloseCommand 그리고 아닙니다 <get_CloseCommand>b__0. 이것은 내부의 익명 메소드의 컴파일러 생성 메소드 이름입니다. get_CloseCommand 방법 ( CloseCommand 얻는 사람). 이것은 위에서 언급 한 요점을 더욱 증명합니다.

다른 팁

나는 지금 다른 줄에 대해 아무것도 몰라요.

CollectionAssert.AreEqual(_vm.ProjectActivities, models);

참조 평등이 사용되어서 실패합니까?

당신은 릴레이 명령에 대한 비교를 무시했지만 관측형 수집에 대한 것은 아닙니다.

대의원의 경우 참조 평등도 사용됩니다.

대신 대의원과 비교해보십시오.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top