Wpf .NET代理平等?

Wpf .NET代理平等?,wpf,mvvm,delegates,equality,relaycommand,Wpf,Mvvm,Delegates,Equality,Relaycommand,无论如何,我认为这就是问题所在。我正在使用一个RelayCommand,它用两个代理装饰一个ICommand。一个是_canExecute的谓词,另一个是_execute方法的操作 ---背景动机-- 动机与演示文稿的单元测试ViewModels有关。一个常见的模式是,我有一个ViewModel,它有一个ObservableCollection,我需要一个单元测试来证明该集合中的数据是给定一些源数据(也需要转换为ViewModels集合)时所期望的数据。尽管两个集合中的数据在调试器中看起来相同,

无论如何,我认为这就是问题所在。我正在使用一个RelayCommand,它用两个代理装饰一个ICommand。一个是_canExecute的谓词,另一个是_execute方法的操作

---背景动机--

动机与演示文稿的单元测试ViewModels有关。一个常见的模式是,我有一个ViewModel,它有一个ObservableCollection,我需要一个单元测试来证明该集合中的数据是给定一些源数据(也需要转换为ViewModels集合)时所期望的数据。尽管两个集合中的数据在调试器中看起来相同,但由于ViewModel的RelayCommand上的相等失败,测试似乎失败了。下面是单元测试失败的一个例子:

[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);
    }
调试器输出:

?_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)}}
-----使用MERHDAD想法的最新编辑

调试器输出 ?项目价值 {Smack.Wpf.ViewModel.RelayCommand} base{SharpArch.Core.DomainModel.ValueObject}:{Smack.Wpf.ViewModel.RelayCommand} _canExecute:空 _执行:{Method={Void}\u executeClose(System.Object)}

这是将代码更改为以下内容后的结果:

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

            return _closeCommand;
        }
    }
    RelayCommand _closeCommand;

    void _executeClose(object param) {
        OnRequestClose();
    }

我现在对其他线路一无所知,但如果

CollectionAssert.AreEqual(_vm.ProjectActivities, models);
仅因为使用了ReferenceEquality而失败

您已覆盖RelayCommand的比较,但未覆盖ObservableCollection的比较

在代理的情况下,似乎也使用了引用相等


试着用Delegate.Method进行比较。

我现在对其他行一无所知,但是如果

CollectionAssert.AreEqual(_vm.ProjectActivities, models);
仅因为使用了ReferenceEquality而失败

您已覆盖RelayCommand的比较,但未覆盖ObservableCollection的比较

在代理的情况下,似乎也使用了引用相等


请尝试按Delegate.Method进行比较。

您是使用匿名函数还是其他方法创建委托的?根据C#规范(§7.9.8),这些是确切的委托相等规则:

委托相等运算符 两个代理实例被视为相等,如下所示: 如果其中一个委托实例为
null
当且仅当两者均为
null

如果代理具有不同的运行时类型,则它们永远不相等。 如果两个代理实例都有一个调用列表(§15.1),则这些实例是相等的,当且仅当它们的调用列表长度相同时,并且一个代理实例的调用列表中的每个条目(定义如下)依次与另一个代理实例的调用列表中的相应条目相等。 以下规则控制调用列表项的相等性:
如果两个调用列表条目都引用了相同的
静态
方法,则条目相等。
如果两个调用列表项都引用了相同目标对象上的相同非
静态方法(由引用相等运算符定义),则这些项相等。
通过使用相同(可能为空)捕获的外部变量实例集对语义相同的匿名函数表达式进行求值而生成的调用列表项允许(但不是必需)相等

因此,在您的例子中,委托实例可能引用两个不同对象中的同一方法,或者引用两个匿名方法


更新:事实上,问题在于调用
new RelayCommand(param=>OnCloseCommand())
时没有传递相同的方法引用。毕竟,这里指定的lambda表达式实际上是一个匿名方法(您没有将方法引用传递给
OnCloseCommand
;您传递的是一个匿名方法引用,该匿名方法采用单个参数并调用
OnCloseCommand
)。正如上面规范引用的最后一行所述,比较这两个委托不必返回
true


旁注:
CloseCommand
属性的getter将被简单地称为
get\u CloseCommand
,而不是
b\u\u 0
。这是编译器为
get\u CloseCommand
方法中的匿名方法生成的方法名(
CloseCommand
getter).这进一步证明了我上面提到的观点。

您是用匿名函数还是什么创建委托的?根据C#规范(§7.9.8),这些是确切的委托相等规则:

委托相等运算符 两个代理实例被视为相等,如下所示: 如果其中一个委托实例为
null
当且仅当两者均为
null

如果代理具有不同的运行时类型,则它们永远不相等。 如果两个代理实例都有一个调用列表(§15.1),则这些实例是相等的,当且仅当它们的调用列表长度相同时,并且一个代理实例的调用列表中的每个条目(定义如下)依次与另一个代理实例的调用列表中的相应条目相等。 以下规则控制调用列表项的相等性:
如果两个调用列表条目都引用了相同的
静态
方法,则条目相等。
如果两个调用列表项都引用了相同目标对象上的相同非
静态方法(由引用相等运算符定义),则这些项相等。
通过使用相同(可能为空)捕获的外部变量实例集对语义相同的匿名函数表达式进行求值而生成的调用列表项允许(但不是必需)相等

因此,在您的情况下,委托实例
    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));
    }


}
?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();
    }
CollectionAssert.AreEqual(_vm.ProjectActivities, models);