C# 优雅地处理基类依赖关系

C# 优雅地处理基类依赖关系,c#,dependency-injection,C#,Dependency Injection,在我正在处理的项目中,我添加了一个基本的ViewModel类,它包含了所有ViewModels通用的一些功能和依赖项。它通过以下属性提供验证、消息传递、调度和导航服务: IValidateProperties Validator { get; } IMessenger Messenger { get; } IDispatcherHelper DispatcherHelper { get; } INavigationService Navigation { get; } 我使用IoC容器连接我的

在我正在处理的项目中,我添加了一个基本的ViewModel类,它包含了所有ViewModels通用的一些功能和依赖项。它通过以下属性提供验证、消息传递、调度和导航服务:

IValidateProperties Validator { get; }
IMessenger Messenger { get; }
IDispatcherHelper DispatcherHelper { get; }
INavigationService Navigation { get; }
我使用IoC容器连接我的依赖项,但是我有几个选项来处理这些依赖项,这些依赖项对于我的所有ViewModels都是通用的:

  • 将它们注入构造函数中。如果我这样做,那么就需要将这四个参数添加到每个ViewModel的构造函数中,并将它们传递给基本构造函数。这给我的代码库增加了很多额外的噪音,我真的宁愿避免。另外,如果我要在某个时候添加另一个依赖项,那就需要更改每个ViewModel的构造函数

  • 使用属性注入。这就是我现在正在使用的方法。不幸的是,这意味着在构建ViewModel之前无法访问这些属性,从而导致以下解决方法:

    private IValidateProperties _validator;
    public IValidateProperties Validator
    {
        get => _validator;
        set
        {
            _validator = value;
            _validator.ValidationTarget = this;
        }
    }
    
    private IMessenger _messenger;
    public IMessenger Messenger
    {
        get => _messenger;
        set
        {
            _messenger = value;
            MessengerAttached();
        }
    }
    
    protected virtual void MessengerAttached() { }
    
  • 将属性设置为静态,并在应用程序启动时注入它们。这对于
    Messenger
    DispatcherHelper
    Navigation
    来说很容易,因为它们都是作为单例使用的。对于
    验证器
    ,我需要添加一个静态
    验证器工厂
    ,并使用工厂在构造函数中实例化
    验证器
    。这种方式似乎是做事情最干净的方式,但我脑海中有这样一种声音告诉我,使用这样的静力学是个坏主意

  • 我觉得选项1是不可能的,因为它会导致大量嘈杂的样板代码添加到我的ViewModels中。我仍然不确定2号还是3号是最好的方式。在这种情况下,使用静力学是一个坏主意,这有什么好的理由吗?还是我什么都不担心

    有些人认为静态会导致不稳定的代码,但在这种情况下,它实际上会使测试更容易。如果使用选项3,我可以为所有视图模型测试添加以下类以进行继承:

    public abstract class ViewModelTestBase
    {
        protected readonly IValidateProperties ValidatorMock;
        protected readonly IMessenger MessengerMock;
        protected readonly IDispatcherHelper DispatcherHelperMock;
        protected readonly INavigationService NavigationMock;
    
        protected ViewModelTestBase()
        {
            ValidatorMock = Substitute.For<IValidateProperties>();
            ViewModelBase.Validator = ValidatorMock;
    
            MessengerMock = Substitute.For<IMessenger>();
            ViewModelBase.Messenger = MessengerMock;
    
            DispatcherHelperMock = Substitute.For<IDispatcherHelper>();
            ViewModelBase.DispatcherHelper = DispatcherHelperMock;
    
            NavigationMock = Substitute.For<INavigationService>();
            ViewModelBase.Navigation = NavigationMock;
        }
    }
    
    公共抽象类ViewModelTestBase
    {
    受保护的只读IValidateProperties;
    受保护的只读IMessenger Messenger Mock;
    受保护的只读IDispatcherHelper DispatcherHelper DispatcherHelperLock;
    受保护的只读INavigationService NavigationMock;
    受保护的ViewModelTestBase()
    {
    ValidatorMock=替换.For();
    ViewModelBase.Validator=ValidatorMock;
    Messenger mock=替换为();
    ViewModelBase.Messenger=MessengerMock;
    DispatcherHelperMock=替换.For();
    ViewModelBase.DispatcherHelper=DispatcherHelperMock;
    NavigationMock=替换.For();
    ViewModelBase.Navigation=NavigationMock;
    }
    }
    

    那么,不采用第三种方法的具体原因是什么?如果静力学在这种情况下真的是个糟糕的主意,那么有什么具体的理由不使用方法2呢

    非常基于意见的问题。。。这是我的1,2,3:使用1,因为这是使用IOC的代码通常期望的方法。很可能没有人会期望IOC为3设置静态属性,而对于2,虽然这是合理的,但感觉您在特定用例的调用时间方面已经存在一些问题(可能试图在构造函数中使用这些属性)…我使用的IOC容器进行属性注入,因此它与选项2一起工作。选项3意味着我必须在应用程序启动时手动设置它们的值,而不是使用IoC容器,但老实说,我对此并不太满意,因为我只需要做一次。我想知道是否真的有什么具体的理由不使用选项3。我希望问题是相反的-什么是使用静态。。。假设您在编写WPF代码时没有进行任何测试,并且永远不会使用多线程或转向编写ASP.Net(或其他可能并行处理多个请求的代码)-您可能有很好的理由坚持使用静态。。。您可能需要阅读或其他一些搜索结果,以获取我推荐的#1。使用静态使代码不容易测试,这与使用依赖项注入的主要原因之一背道而驰。是的,这将是一些额外的样板代码,但它很容易编写。您可以创建一个facade服务,如
    ViewModelManager
    ,它封装了所有这些常见的依赖项,因此您只需处理视图模型构造函数的一个额外参数。这也可以避免在以后创建另一个依赖项时向视图模型构造函数添加额外的参数。#1也是我的建议。有关说明和详细信息,请参阅。这也适用于未来的更改和增强,只需很少的努力。非常基于意见的问题。。。这是我的1,2,3:使用1,因为这是使用IOC的代码通常期望的方法。很可能没有人会期望IOC为3设置静态属性,而对于2,虽然这是合理的,但感觉您在特定用例的调用时间方面已经存在一些问题(可能试图在构造函数中使用这些属性)…我使用的IOC容器进行属性注入,因此它与选项2一起工作。选项3意味着我必须在应用程序启动时手动设置它们的值,而不是使用IoC容器,但老实说,我对此并不太满意,因为我只需要做一次。我想知道是否真的有什么具体的理由不使用选项3。我希望问题是相反的-什么是使用静态。。。假设您在编写WPF代码时没有进行任何测试,并且永远不会使用多线程或转向编写ASP.Net(或其他可能并行处理多个请求的代码)-您可能有很好的理由坚持使用静态。。。你可能想要阅读或其他一些东西