سؤال

أعتقد أن هذا هو السؤال، على أية حال.أنا أستخدم RelayCommand، الذي يزين ICommand بمندوبين اثنين.أحدهما هو المسند لـ _canExecute والآخر هو الإجراء للأسلوب _execute.

---الخلفية الدافعة ---

الدافع يتعلق باختبار الوحدة ViewModels لـ WPF عرض تقديمي.النمط المتكرر هو أن لدي ViewModel واحد يحتوي على ObservableCollection، وأريد اختبار وحدة لإثبات أن البيانات الموجودة في تلك المجموعة هي ما أتوقعه في ضوء بعض البيانات المصدر (والتي تحتاج أيضًا إلى تحويلها إلى مجموعة من ViewModels).على الرغم من أن البيانات الموجودة في كلتا المجموعتين تبدو متماثلة في مصحح الأخطاء، يبدو أن الاختبار فشل بسبب فشل المساواة في RelayCommand الخاص بـ 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);
    }

--- العودة إلى تفويض المساواة ----

إليك رمز RelayCommand - وهو في الأساس سرقة مباشرة لفكرة جوش سميث، مع تطبيق للمساواة أضفته في محاولة لحل هذه المشكلة:

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 بشكل فعال على نفس الطريقة (_canExecute فارغ في كلتا الحالتين)، يفشل اختبار الوحدة في هذا السطر:

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

هل يمكن لأي شخص أن يشرح ما أفتقده وما هو الإصلاح؟

---- ملاحظات معدلة ----

كما أشار مهرداد، يبدو get_CloseCommand من جلسة تصحيح الأخطاء غريبًا بعض الشيء في البداية.إنها حقًا مجرد ملكية، ولكنها تثير نقطة حول سبب كون المساواة بين المندوبين مشكلة إذا كنت بحاجة إلى القيام بحيل لإنجاحها.

بعض نقاط MVVM هي كشف كل ما قد يكون مفيدًا في العرض التقديمي كخصائص، حتى تتمكن من استخدام ربط WPF.تحتوي الفئة المعينة التي كنت أختبرها على WorkspaceViewModel في تسلسلها الهرمي، وهو مجرد ViewModel يحتوي بالفعل على خاصية أمر إغلاق.هنا هو الرمز:

فئة مجردة عامة 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);
            }
        }
    }
}

يمكنك أن ترى أن أمر الإغلاق هو أمر RelayCommand، وأنني قمت بالتلاعب مع يساوي لجعل اختبار الوحدة يعمل.

Merhdad هنا هو اختبار الوحدة الذي يعمل فقط عندما أستخدم مندوب Trickster.method في مقارنة المساواة.

TestFixture] فئة العمل العامة فئة العمل {Private WorkspaceViewModel VM1 ؛خاص 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));
    }


}

----- أحدث التعديلات لاستخدام فكرة مرهداد

Debugger out put؟ valueofThisObject {smack.wpf.viewmodel.relayCommand} base {Sharparch.core.domainmodel.valueObject}:{smack.wpf.viewmodel.relaycommand} _canexecute:null _execute:{الطريقة = {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

وهذه هي النتيجة بعد تغيير الكود إلى:

    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 طريقة على ال نفس الكائن المستهدف (كما هو محدد بواسطة عوامل المساواة المرجعية) فإن الإدخالات متساوية.
إدخالات قائمة الاستدعاء الناتجة عن تقييم متطابقة لغويا تعبيرات الوظيفة المجهولة مع نفس المجموعة (ربما الفارغة) من مثيلات المتغير الخارجي التي تم التقاطها نكون مسموح به (لكنه غير مطلوب) لتكون مساوية.

لذلك، في حالتك، من الممكن أن تشير مثيلات المفوض إلى نفس الطريقة في كائنين مختلفين، أو تشير إلى طريقتين مجهولتين.


تحديث: في الواقع، المشكلة هي أنك لا تمرر نفس مرجع الطريقة عند الاتصال new RelayCommand(param => OnCloseCommand()).بعد كل شيء، تعبير لامدا المحدد هنا هو في الواقع طريقة مجهولة (لا تقوم بتمرير مرجع الطريقة إلى OnCloseCommand;أنت تقوم بتمرير مرجع إلى طريقة مجهولة تأخذ معلمة واحدة ومكالمات OnCloseCommand).كما هو مذكور في السطر الأخير من اقتباس المواصفات أعلاه، ليس من الضروري مقارنة هذين المندوبين true.

ملاحظة جانبية: الحاصل على CloseCommand سيتم استدعاء الملكية ببساطة get_CloseCommand و لا <get_CloseCommand>b__0.هذا هو اسم الطريقة التي أنشأها المترجم للطريقة المجهولة بالداخل get_CloseCommand طريقة ( CloseCommand جالب).وهذا يثبت كذلك النقطة التي ذكرتها أعلاه.

نصائح أخرى

وأنا لا أعرف أي شيء الآن حول خطوط أخرى ولكن ماذا لو

CollectionAssert.AreEqual(_vm.ProjectActivities, models);

وفشل لمجرد يستخدم ReferenceEquality؟

ولقد تجاوز مقارنة لRelayCommand ولكن ليس لObservableCollection.

ويبدو في حالة المندوبين مرجع يستخدم المساواة أيضا.

وحاول المقارنة بينها حسب Delegate.Method بدلا من ذلك.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top