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

C# 为什么C标准库(mscorlib)不为其类提供抽象?,c#,dependency-injection,mocking,abstraction,C#,Dependency Injection,Mocking,Abstraction,C标准库目前没有为其大多数产品提供抽象/接口,如文件、HttpClient等。事实上,我不确定VB.NET、F等的情况是否如此,所以现在让我们继续使用C。然而,需求是相当高的,因为单元测试实际上需要模拟对IO、网络等的依赖性。有相当多的自制解决方案,大多数都是包装器 我很好奇的是,为什么标准库没有简单地通过在库中包含接口来简化这一过程。是否有设计上的原因?风格?很简单,微软在第一次构建.NET框架时,没有预料到依赖注入和单元测试的广泛使用 但这是一个常见问题,不仅对于本机.NET类型,对于第三方

C标准库目前没有为其大多数产品提供抽象/接口,如文件、HttpClient等。事实上,我不确定VB.NET、F等的情况是否如此,所以现在让我们继续使用C。然而,需求是相当高的,因为单元测试实际上需要模拟对IO、网络等的依赖性。有相当多的自制解决方案,大多数都是包装器


我很好奇的是,为什么标准库没有简单地通过在库中包含接口来简化这一过程。是否有设计上的原因?风格?

很简单,微软在第一次构建.NET框架时,没有预料到依赖注入和单元测试的广泛使用

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

示例:UrlHelper 在MVC4和之前,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

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

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

正如您已经发现的,有很多库通过构建自己的适配器来为您完成这些工作,如果可以节省一些步骤,那么使用它们也没有什么错

正如注释中提到的,IO.Stream已经是一个抽象,因此在这种特定情况下没有必要


首先,文件是静态的……好吧,也许不是最好的例子。System.IO.Stream呢?此外,我认为即使像File或Directory这样的类是静态的,我认为我的观点仍然成立-IO依赖性通常需要隔离,但访问它们的标准库方式是静态的?IO.Stream不是抽象的吗?除此之外,在这些情况下,Microsoft必须提供的抽象是非常通用的,它们仍然不适合您的应用程序需要。这将妨碍应用程序的设计和可测试性。换句话说,您的应用程序仍然会在很大程度上违反依赖倒置原则。相反,答案是正如NightOwl已经非常正确地指出的那样,它定义了您自己指定的抽象的适配器。
public class UrlHelperAdapter : UrlHelper, IUrlHelper
{
    private UrlHelperAdapter(RequestContext requestContext)
        : base(requestContext)
    {
    }

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