【问题标题】:How fast is class instantiation with methods, but no fields or properties?使用方法但没有字段或属性的类实例化有多快?
【发布时间】:2012-03-30 07:00:08
【问题描述】:

一般来说,使用方法但没有字段或属性的类的实例化是否有很多开销?

我正在开发一个 ASP.NET MVC 应用程序,该应用程序大量使用构造函数注入,到目前为止,一些控制器有多达 10 个依赖项。但由于依赖项数量众多,我使用了一个 IMyAppServiceProvider 接口和类,通过 MVC 3 中的 DependencyResolver 提供对所有依赖项的通用访问。

我删除了所有应用程序特定的代码,并使用我的基本设置创建了一个Gist(但这不包括下面提到的 BaseController 设置)。

我还创建了一个接受IMyAppServiceProvider 的 BaseController 类。所有控制器都继承自这个基类。基类采用IMyAppServiceProvider 对象并为所有各种服务提供受保护的变量。代码如下所示:

public class BaseController
{
    protected IService1 _service1;
    protected IService2 _service2;
    protected IService3 _service3;
    // ...

    public BaseController(IMyAppServiceProvider serviceProvider)
    {
        _service1 = serviceProvider.GetService<IService1>;
        _service2 = serviceProvider.GetService<IService2>;
        _service3 = serviceProvider.GetService<IService3>;
        // ...
    }
}

这使得控制器的代码“干净利落”。没有私有/受保护变量,构造函数中没有赋值,并且服务由基类受保护变量引用。 但是,每个请求都会实例化我的应用程序使用的每一个服务,无论特定控制器是否使用所有这些服务。

我的服务很简单,只包含带有一些业务逻辑和数据库交互的方法调用。它们是无状态的,没有类字段或属性。因此,实例化应该很快,但我想知道这是否是最佳实践(我知道这是一个加载术语)。

【问题讨论】:

  • 一如既往,首先是个人资料。但是,我猜测解决依赖关系比实例化它们慢几个数量级。
  • @insta 哇,我没有透过树林看到森林。但是 Ninject 和/或其他 IoC 容器缓存依赖解析吗?
  • 我在下面添加了一个可以帮助你的答案......我之前也遇到过同样的问题。大多数人(包括我自己)不知道您可以覆盖控制器工厂 ASP.NET MVC 用于创建控制器。自定义容器可以紧密集成您的 IoC 容器,并根据需要处理依赖项。
  • 每个类 10 个依赖项?请阅读这个问题:stackoverflow.com/questions/9686567/dependency-injection-what-to-do-when-you-have-a-lot-of-dependencies,以及这个问题:stackoverflow.com/questions/2420193/…

标签: c# .net asp.net-mvc dependency-injection service-locator


【解决方案1】:

每个请求都会实例化我的每一个服务 应用程序使用,无论特定控制器是否使用所有 他们。

我相信您自己已经回答了您的问题,这不是一个好方法。 此外,使用这种依赖解析(服务定位器注入)是一种不好的做法,因为控制器的 API 变得混乱。控制器客户端不知道特定控制器真正需要哪些服务,因此您最终可能会遇到意外的运行时错误,单元测试也会一团糟。

顺便说一句,还有一个建议——用abstract关键字标记所有被认为是基类的类,这样你就可以避免将它用作具体类。设计和实现基类是一项具体的设计决策,因此请明确您的设计意图。

关于实例化的成本,在您的情况下不会有很大的不同,但一般来说,为了降低重物实例化的成本,您可以:

  • 使用Prototype pattern“避免以标准方式(例如,使用'new' 关键字)创建新对象的内在成本,因为它对于给定的应用程序来说过于昂贵”( c)Wikipedia
  • Lazy Initialization 用于从所有者对象生命周期开始就不需要的服务,因此这些服务将按需初始化。由于 .NET Framework 4.0 你可以使用内置的Lazy(T) class

【讨论】:

  • 很好的答案,但我想为自己辩护一点。我的控制器 API 现在非常干净,因为我将所有依赖项抽象到服务定位器中。我可以为每个控制器做一个更具体的接口,但对于我的特定应用程序/负载,它是矫枉过正的。此外,我不会遇到运行时错误,因为我的单元测试确保代码运行并正确运行。但我确实看到你关于缺乏依赖意识的观点。现在我已经这样做了,我没有简单的方法来了解 Controller 的依赖项(供我自己参考)。暂时没关系。
【解决方案2】:

我认为您在这里寻找的解决方案是使用自定义控制器工厂。这样,创建的每个控制器都具有它需要的依赖项。这是 StructureMap 的一个,来自weblogs.asp.net

using StructureMap; 
public class StructureMapControllerFactory : DefaultControllerFactory { 

    protected override IController GetControllerInstance(Type controllerType) {
        try {
           return ObjectFactory.GetInstance(controllerType) as Controller;
        }
        catch (StructureMapException) {
            System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
            throw;
        }
    }
}

protected void Application_Start() {
    RegisterRoutes(RouteTable.Routes);

    //Configure StructureMapConfiguration
    // TODO: config structuremap        

    //Set current Controller factory as StructureMapControllerFactory
    ControllerBuilder.Current.SetControllerFactory(new StructureMapControllerFactory()); 
}

【讨论】:

  • 感谢您的回复,但我想我已经有了这个?以前我设置了控制器的构造函数来接受其服务依赖项的接口。 Ninject 和它的 Bootstrapper 来完成这个。
  • 那么,接受所有依赖项的基本控制器的意义何在?如果您的控制器已被修剪为仅采用他们需要的构造函数参数,但它们已被填充,那么这是您能做的最好的事情。
  • 因此,我在 BaseController 中处理一次,而不是在构造函数主体中拥有 10 个私有字段、10 个构造函数参数和 10 个赋值。这样,当我添加新的依赖项时,Controller 构造函数不会改变。相反,BaseController 将在其构造函数中获得一个新字段和赋值。同样,这也简化了我的测试设置。我有一个 BaseTests 类,可以在一个地方为每个模拟设置字段。
猜你喜欢
  • 2019-03-11
  • 1970-01-01
  • 2011-12-06
  • 1970-01-01
  • 2021-06-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
  • 1970-01-01
相关资源
最近更新 更多