为什么C#标准库(mscorlib)没有为其类提供抽象?

时间:2017-01-16 11:21:22

标签: c# dependency-injection mocking abstraction

C#标准库目前不提供大多数产品的抽象/接口,例如FileHttpClient等。(实际上,我不确定是这种情况VB.NET,F#等,所以现在让我们坚持使用C#。)然而,需求相当高,因为单元测试实际上要求你模拟IO,网络等的依赖关系。有很多自制解决方案,其中大多数是“包装纸”。

我很好奇的是,为什么标准库不会通过简单地包含库中的接口来简化这一过程。有设计原因吗?文体?

1 个答案:

答案 0 :(得分:1)

很简单,当他们第一次构建.NET框架时,微软无法预料到依赖注入和单元测试的广泛使用。

但这是一个常见问题,不仅适用于本机.NET类型,也适用于第三方库中的类型。处理没有抽象的类型的常用技术是使用adapter pattern来进行抽象。然后,您只需要使用内置类型而不是库类型。

示例:UrlHelper

在MVC 4中,UrlHelper类之前没有虚拟成员,因此无法进行模拟或注入。要解决这个问题,只需要创建一个具有相同成员的接口(这可以使用"提取接口" Visual Studio中的功能轻松完成...)

public interface IUrlHelper
{
    string Action(string actionName);
    string Action(string actionName, object routeValues);
    string Action(string actionName, string controllerName);
    string Action(string actionName, string controllerName, object routeValues);
    string Action(string actionName, string controllerName, object routeValues, string protocol);
    string Action(string actionName, string controllerName, RouteValueDictionary routeValues);
    string Action(string actionName, string controllerName, RouteValueDictionary routeValues, string protocol, string hostName);
    string Action(string actionName, RouteValueDictionary routeValues);
    string Content(string contentPath);
    string Encode(string url);
    RequestContext RequestContext { get; }
    RouteCollection RouteCollection { get; }
    string RouteUrl(object routeValues);
    string RouteUrl(string routeName);
    string RouteUrl(string routeName, object routeValues);
    string RouteUrl(string routeName, object routeValues, string protocol);
    string RouteUrl(string routeName, RouteValueDictionary routeValues);
    string RouteUrl(string routeName, RouteValueDictionary routeValues, string protocol, string hostName);
    string RouteUrl(RouteValueDictionary routeValues);
    bool IsLocalUrl(string url);
    string HttpRouteUrl(string routeName, object routeValues);
    string HttpRouteUrl(string routeName, RouteValueDictionary routeValues);
}

...然后创建一个继承UrlHelper并实现IUrlHelper的适配器类型......

public class UrlHelperAdapter : UrlHelper, IUrlHelper
{
    private UrlHelperAdapter(RequestContext requestContext)
        : base(requestContext)
    {
    }

    public UrlHelperAdapter(RequestContext requestContext, RouteCollection routeCollection)
        : base(requestContext, routeCollection)
    {
    }
}

完成此操作后,您可以将IUrlHelper传递给任何方法或构造函数,并且只需要传递UrlHelperAdapter作为具体类型(或者如果使用模拟框架,则构建模拟{{1} 1}})。

这种技术适用于大多数没有抽象的非静态类型。

正如您已经发现的那样,有一些完整的库通过构建自己的适配器为您做腿部工作,如果它可以为您节省一些步骤,那么使用它们没有任何问题。

  

正如评论中所提到的,IUrlHelper已经 是一个抽象,因此在特定情况下没有必要。