【半小时大话.net依赖注入】(一)理论基础+实战控制台程序实现AutoFac注入

  1. 第一章|理论基础+实战控制台程序实现AutoFac注入

  2. 第二章|AutoFac的常见使用套路

  3. 第三章|实战Asp.Net Framework Web程序实现AutoFac注入

  4. 第四章|实战Asp.Net Core自带DI实现依赖注入

  5. 第五章|实战Asp.Net Core引入AutoFac的两种方式

简介

该系列共5篇文章,旨在以实战模式,在.net下的

  • 控制台程序

  • Framework Mvc程序

  • Framework WebApi程序

  • Core Api程序

分别实现依赖注入。

其中.Net Framework框架主要以如何引入AutoFac作为容器以及如何运用AuotoFac为主,.Net Core框架除了研究引入AutoFac的两种方式,同时也运用反射技巧对其自带的DI框架进行了初步封装,实现了相同的依赖注入效果。
项目架构如下图:

640?wx_fmt=png

Ray.EssayNotes.AutoFac.Infrastructure.CoreIocCore容器类库.NET Core 2.2
Ray.EssayNotes.AutoFac.Infrastructure.IocFramework容器类库.NET Framework 4.5
Ray.EssayNotes.AutoFac.Model实体层类库.NET Framework 4.5
Ray.EssayNotes.AutoFac.Repository仓储层类库.NET Framework 4.5
Ray.EssayNotes.AutoFac.Service业务逻辑层类库.NET Framework 4.5
Ray.EssayNotes.AutoFac.ConsoleApp控制台主程序控制台项目.NET Framework 4.5
Ray.EssayNotes.AutoFac.CoreApiCore WebApi主程序Core Api项目.NET Core 2.2
Ray.EssayNotes.AutoFac.NetFrameworkApiFramework WebApi主程序Framework WebApi项目.NET Framework 4.5
Ray.EssayNotes.AutoFac.NetFrameworkMvcFramework MVC主程序Framework MVC项目.NET Framework 4.5

GitHub源码地址:https://github.com/WangRui321/Ray.EssayNotes.AutoFac

Welcome to fork me~(欢迎来叉我~)

适用对象

该项目主要实战为主,理论部分我会结合例子和代码,深入浅出地阐述,如果你是:

  • 从来没听过IoC、DI这些劳什子

  • 了解一些依赖注入的理论知识但是缺乏实战

  • 在.Net Framework下已熟练运用依赖注入,但在.Net Core还比较陌生

只要你花上半个小时认真读完每一句话,我有信心这篇文章一定会对你有所帮助。

如果你是:

那么也欢迎阅读,虽然可能对你帮助并不大,但是欢迎提供宝贵的意见,有写的不好的地方可以互相交流~

下面开始第一章《理论知识+实战控制台程序实现AutoFac注入》


依赖

依赖,简单说就是,当一个类需要另一个类协作来完成工作的时候就产生了依赖。这也是耦合的一种形式。

举个例子,比如标准的三层架构模式

界面层(UI)负责展示数据StudentController
业务逻辑层(BLL)负责业务逻辑运算StudentService
数据访问层(DAL)负责提供数据StudentRepository

数据访问层(DAL)代码:

                public class StudentRepository    {        public string GetName(long id)        {            return "学生张三";        }    }

业务层(BLL)代码:

                public class StudentService    {        private readonly StudentRepository _studentRepository;        public StudentService()        {            _studentRepository = new StudentRepository();        }        public string GetStuName(long id)        {            var stu = _studentRepository.Get(id);            return stu.Name;        }    }

其中,StudentService的实现,就必须要依赖于StudentRepository。而且这是一种紧耦合,一旦StudentRepository有任何更改,必然导致StudentService的代码同样也需要更改,这种情况是程序员们不愿意看到的。

640?wx_fmt=jpeg

接口驱动

接口驱动是为了实现一个设计原则:要依赖于抽象,而不是具体的实现
还拿上面的例子说明,现在我添加一个DAL的接口层,IStudentRepository,抽象出所需方法:

                public interface IStudentRepository    {        string GetName(long id);    }

然后让StudentRepository去实现这个接口:

                public class StudentRepository : IStudentRepository    {        public string GetName(long id)        {            return "学生张三";        }    }

然后在StudentService里只依赖于IStudentRepository,以后的增删改查都通过IStudentRepository这个抽象来做:

                public class StudentService    {        private readonly IStudentRepository _studentRepository;        public StudentService()        {            _studentRepository = new StudentRepository();        }        public string GetStuName(long id)        {            var stu = _studentRepository.Get(id);            return stu.Name;        }    }

这样做的好处有两个,一个是低耦合,一个是职责清晰。如果对此还有怀疑的话,我们可以想象一个情景,就是负责写StudentService的是程序员A,负责写StudentRepository的是另一个程序员B,那么:

  • 针对程序员A

我(程序员A)只需要关注业务逻辑层面,如果我需要从仓储层拿数据库的数据,比如我需要根据Id获取学生实体,那么我只需要去IStudentRepository找Get(long id)函数就可以了,至于实现它的仓储怎么实现这个方法我完全不用管,你怎么从数据库拿数据不是我该关心的事情。

  • 针对程序员B

我(程序员B)的工作就是实现IStudentRepository接口的所有方法就行了,简单而明确,至于谁来调用我,我不用管。IStudentRepository里有根据Id获取学生姓名的方法,我实现了就行,至于业务逻辑层拿这个名字干啥,那不是我要关心的事情。

这样看的话是不是彼此的职责就清晰多了,更进一步再举个极端的例子:
比如程序员B是个实习生,整天划水摸鱼,技术停留在上个世纪,结果他写的仓储层读取数据库全部用的手写sql语句的方式,极难维护,后来被领导发现领了盒饭,公司安排了另一个程序员C来重写仓储层,C这时不需要动其他代码,只需要新建一个仓储StudentNewRepository,然后实现之前的IStudentRepository,C使用Dapper或者EF,写完新的仓储层之后,剩下的只需要在StudentService里改一个地方就行了:

        public StudentService()        {            _studentRepository = new StudentNewRepository();        }

是不是很清晰,耦合不会像以前那么重。
其实对于这个小例子来说,接口驱动的优势还不太明显,但是在系统层面优势就会被放大。比如上面换仓储的例子,虽然职责是清晰了,但是项目里有几个Service就需要改几个地方,还是很麻烦。原因就是上面讲的,这是一种依赖关系,Service要依赖Repository,有没有一种方法可以让这种控制关系反转过来呢?当Service需要使用Repository,有没有办法让我需要的Repository自己注入到我这里来?
当然有,这就是我们将要实现的依赖注入。使用依赖注入后你会发现,当C写完新的仓储后,业务逻辑层(StudentService)是不需要改任何代码的,所有的Service都不需要一个一个去改,直接在注入的时候修改规则,不要注入以前老的直接注入新的仓储就可以了。

面向接口后的架构:

界面层(UI)负责展示数据StudentController
业务逻辑抽象层(InterfaceBLL)业务逻辑运算抽象接口IStudentService
业务逻辑层(BLL)负责业务逻辑运算StudentService
数据访问抽象层(InterfaceDAL)数据访问抽象接口IStudentRepository
数据访问层(DAL)负责提供数据StudentRepository

什么是IoC

IoC,全称Inversion of Control,即“控制反转”,是一种设计原则,最早由Martin Fowler提出,因为其理论提出时间和成熟时间相对较晚,所以并没有被包含在GoF的《设计模式》中。

什么是DI

DI,全称Dependency Injection,即依赖注入,是实现IoC的其中一种设计方法。
其特征是通过一些技巧,将依赖的对象注入到调用者当中。(比如把Repository注入到Service当中)
这里说的技巧目前主要指的就是引入容器,先把所有会产生依赖的对象统一添加到容器当中,比如StudentRepository和StudentService,把分配权限交给容器,当StudentService内部需要使用StudentRepository时,这时不应该让它自己new出来一个,而是通过容器,把StudentRepository注入到StudentService当中。
这就是名称“依赖注入”的由来。

640?wx_fmt=jpeg

DI和IoC有什么区别

这是个老生常谈的问题了,而且这两个名字经常在各种大牛和伪大牛的吹逼现场频繁出现 ,听的新手云里雾里,莫名感到神圣不可侵犯。那么DI和IoC是同一个东西吗?如果不是,它们又有什么区别呢?
回答很简单:不是一个东西
区别也很简单,一句话概括就是:IoC是一种很宽泛的理念,DI是实现了IoC的其中一种方法
说到这里我已经感觉到屏幕后的你性感地添了一下嘴唇,囤积好口水,准备开始喷我了。
先别慌,我有证据,我们先来看下微软怎么说:

ASP.NET Core supports the dependency injection (DI) software design pattern, which is a technique for achieving Inversion of Control (IoC) between classes and their dependencies.

地址:https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2

翻译过来就是“ASP.NET Core支持依赖注入(DI)的软件设计模式,该模式是一种在类和它依赖的对象之间实现了控制反转(IoC)的技术”。

如果有人觉得辣鸡微软不够权威,那我们去看下IoC以及DI这两个概念的发明人——Martin Fowler怎么说:

几位轻量级容器的作者曾骄傲地对我说:这些容器非常有用,因为它们实现了控制反转。这样的说辞让我深感迷惑:控制反转是框架所共有的特征,如果仅仅因为使用了控制反转就认为这些轻量级容器与众不同,就好象在说我的轿车是与众不同的,因为它有四个轮子。
因此,我想我们需要给这个模式起一个更能说明其特点的名字——”控制反转”这个名字太泛了,常常让人有些迷惑。经与多位IoC 爱好者讨论之后,我们决定将这个模式叫做”依赖注入”(Dependency Injection)。

地址:http://insights.thoughtworkers.org/injection/

Martin Fowler说的比较委婉,其实说白了就是建议我们,不要乱用IoC装逼,IoC是一种设计理念,很宽泛,你把程序里的一个写死的变量改成从配置文件里读取也是一种控制反转(由程序控制反转为由框架控制),你把这个配置改成用户UI界面的一个输入文本框由用户输入也是一种控制反转(由框架控制反转为由用户自己控制
所以,如果确定讨论的模式是DI,那么就表述为DI,还是尽量少用IoC这种宽泛的表达。

AutoFac

AutoFac是一个开源的轻量级的DI容器,也是.net下最受大家欢迎的实现依赖注入的工具之一,通过AutoFac我们可以很方便的实现一些DI的骚操作。

目标很简单,就是控制台程序启动后,将学生姓名打印出来。
程序启动流程是,控制台主程序调用Service层,Service层调用Repository层获取数据(示例项目的仓储层没有连接数据库,只是直接造个假数据返回)。
没有依赖注入的情况下,肯定是主程序会new一个StudentService,StudentService里会new一个StudentRepository,现在引入依赖注入后,就不应该这么new出来了,而是通过容器注入,也就是容器会把StudentRepository自动注入到StudentService当中。

架构

实体层

640?wx_fmt=png

学生实体类StudentEntity:

namespace Ray.EssayNotes.AutoFac.Model{        public class StudentEntity    {                public long Id { get; set; }                public string Name { get; set; }                public int Grade { get; set; }    }}

仓储层

640?wx_fmt=png

IStudentRepository接口:

using Ray.EssayNotes.AutoFac.Model;namespace Ray.EssayNotes.AutoFac.Repository.IRepository{        public interface IStudentRepository    {        string GetName(long id);    }}

StudentRepository仓储类:

using Ray.EssayNotes.AutoFac.Model;using Ray.EssayNotes.AutoFac.Repository.IRepository;namespace Ray.EssayNotes.AutoFac.Repository.Repository{                public class StudentRepository : IStudentRepository    {        public string GetName(long id)        {            return "学生张三";        }    }}

Service层

640?wx_fmt=png

IStudentService接口

namespace Ray.EssayNotes.AutoFac.Service.IService{                public interface IStudentService    {        string GetStuName(long id);    }}

StudentService类:

using Ray.EssayNotes.AutoFac.Repository.IRepository;using Ray.EssayNotes.AutoFac.Repository.Repository;using Ray.EssayNotes.AutoFac.Service.IService;namespace Ray.EssayNotes.AutoFac.Service.Service{                public class StudentService : IStudentService    {        private readonly IStudentRepository _studentRepository;                                        public StudentService(IStudentRepository studentRepository)        {            _studentRepository = studentRepository;        }        public string GetStuName(long id)        {            var stu = _studentRepository.Get(id);            return stu.Name;        }    }}

其中构造函数是一个有参的函数,参数是学生仓储,这个后面依赖注入时会用。

AutoFac容器

640?wx_fmt=png

需要先通过Nuget导入Autofac包:

640?wx_fmt=png

using System;using System.Reflection;using Autofac;using Autofac.Core;using Ray.EssayNotes.AutoFac.Repository.IRepository;using Ray.EssayNotes.AutoFac.Repository.Repository;using Ray.EssayNotes.AutoFac.Service.IService;using Ray.EssayNotes.AutoFac.Service.Service;namespace Ray.EssayNotes.AutoFac.Infrastructure.Ioc{                public static class Container    {                                public static IContainer Instance;                                        public static void Init()        {                        var builder = new ContainerBuilder();                        MyBuild(builder);                        Instance = builder.Build();        }                                        public static void MyBuild(ContainerBuilder builder)        {            builder.RegisterType<StudentRepository>().As<IStudentRepository>();            builder.RegisterType<StudentService>().As<IStudentService>();        }    }}

其中:

  • public static IContainer Instance
    为单例容器

  • Init()方法
    用于初始化容器,即往容器中添加对象,我们把这个添加的过程称为注册(Register)。
    ContainerBuilder为AutoFac定义的容器构造器,我们通过使用它往容器内注册对象。

  • MyBuild(ContainerBuilder builder)方法
    我们具体注册的实现函数。RegisterType是AutoFac封装的一种最基本的注册方法,传入的泛型(StudentService)就是我们欲添加到容器的对象;As函数负责绑定注册对象的暴露类型,一般是以其实现的接口类型暴露,这个暴露类型是我们后面去容器内查找对象时使用的搜索标识,我们从容器外部只有通过暴露类型才能找到容器内的对象。

    主程序

640?wx_fmt=png

需要先Nuget导入AutoFac程序包:

640?wx_fmt=png

using System;using Autofac;using Ray.EssayNotes.AutoFac.Infrastructure.Ioc;using Ray.EssayNotes.AutoFac.Service.IService;namespace Ray.EssayNotes.AutoFac.ConsoleApp{    class Program    {        static void Main(string[] args)        {            Container.Init();            PrintStudentName(10001);            Console.ReadKey();        }                                        public static void PrintStudentName(long id)        {                        IStudentService stuService = Container.Instance.Resolve<IStudentService>();            string name = stuService.GetStuName(id);            Console.WriteLine(name);        }     } }

进入Main函数,先调用容器的初始化函数,该函数执行成功后,StudentRepository和StudentService就被注册到容器中了。
然后调用打印学生姓名的函数,其中Resolve()方法是AutoFac封装的容器的解析方法,传入的泛型就是之前注册时的暴露类型,下面可以详细看下这一步到底发生了哪些事情:

  • 容器根据暴露类型解析对象

也就是容器会根据暴露类型IStudentService去容器内部找到其对应类(即StudentService),找到后会试图实例化一个对象出来。

  • 实例化StudentService

AutoFac容器在解析StudentService的时候,会调用StudentService的构造函数进行实例化。

  • 构造注入

AutoFac容器发现StudentService的构造函数需要一个IStudnetRepository类型的参数,于是会自动去容器内寻找,根据这个暴露类型找到对应的StudnetRepository后,自动将其注入到了StudentService当中

经过这几步,一个简单的基于依赖注入的程序就完成了。

结果

我们将控制台程序设置为启动项目,点击运行,如图调用成功:

640?wx_fmt=png

如果把调试断点加在容器初始化函数里,可以很清晰的看到哪些对象被注册到了容器里:

640?wx_fmt=png

使用控制台程序本来是为了突出容器的概念,但是容易造成一些误解,DI的最终形态可以参考源码里的Api项目和MVC项目,本来想循序渐进,先第一章控制台引入容器的概念,然后第二章讲批量注册、注入泛型、生命周期域管理,第三章讲Api和MVC项目,最后两章讲下.net core的DI,但是这里还是先说下吧:

  • 误解1:每次添加Service和Repository都要去注册,不是更麻烦?

其实是不需要一个一个注册的,运用批量注册后容器内部的代码是这样的,可以直接批量注册所有的:

            public static class MvcContainer    {        public static IContainer Instance;                public static void Init(Func<ContainerBuilder, ContainerBuilder> func = null)        {                        var builder = new ContainerBuilder();                        MyBuild(builder);             func?.Invoke(builder);                        Instance = builder.Build();                        System.Web.Mvc.DependencyResolver.SetResolver(new AutofacDependencyResolver(Instance));        }        public static void MyBuild(ContainerBuilder builder)        {            Assembly[] assemblies = Helpers.ReflectionHelper.GetAllAssembliesWeb();                        builder.RegisterAssemblyTypes(assemblies)                .Where(cc => cc.Name.EndsWith("Repository") |                             cc.Name.EndsWith("Service"))                .PublicOnly()                .Where(cc => cc.IsClass)                .AsImplementedInterfaces();                        builder.RegisterGeneric(typeof(BaseRepository<>)).As(typeof(IBaseRepository<>));                        Assembly mvcAssembly = assemblies.FirstOrDefault(x => x.FullName.Contains(".NetFrameworkMvc"));            builder.RegisterControllers(mvcAssembly);        }    }

误解2:每次使用都要解析下,还不如直接new
好吧,其实也是不需要自己去解析的,最终形态的Controller入口是这样的,直接在构造函数里写就行了:

    public class StudentController : Controller    {        private readonly IStudentService _studentService;        public StudentController(IStudentService studentService)        {            _studentService = studentService;        }                                                public string GetStuNameById(long id)        {            return _studentService.GetStuName(id);        }    }

就是直接在构造函数里注入就可以了。

  • 误解3:依赖注入是不是过度设计?

首先DI是一个设计模式(design pattern),其本身完全不存在过不过度的问题,这完全取决于用的人和怎么用。
另外,在.NET Core中,DI被提到了一个很重要的地位,如果想要了解.NET Core,理解DI是必不可少的。

原文地址:https://www.cnblogs.com/RayWang/p/11128554.html


.NET社区新闻,深度好文,欢迎访问公众号文章汇总 http://www.csharpkit.com 

640?wx_fmt=jpeg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/315018.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

P4198 楼房重建 线段树 + 区间合并

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 题面有点问题&#xff0c;按照人类正常的理解来就好啦。 思路&#xff1a; 可以想到维护每个位置的一个斜率&#xff0c;模拟的话就是从第一个位置开始向后选&#xff0c;当某个位置斜率大于当前位置的时…

基于 Docker Compose 实践 .NET Core 的现代化架构 2:在容器中集成 Skywalking APM

本章节代码已经上传至 https://github.com/siegrainwong/.NET-Core-with-Docker/tree/master/Part2系列大纲还是先介绍一下目录&#xff0c;这次我们讲第二篇&#xff1a;用 docker-compose 启动 WebApi 和 SQL Server在容器中集成 Skywalking APM通过 nginx-proxy 对 ES、Skyw…

使用Minikube部署本地Kubernetes集群(二十九)

前言 使用Minikube部署本地k8s集群相对比较简单&#xff0c;非常推荐将其用于本地k8s开发环境&#xff0c;唯一麻烦点的仅仅是网络问题。在本篇教程中&#xff0c;我们使用了国内的镜像来完成本地k8s集群的搭建。如果搭建过程中出现错误&#xff0c;建议使用mini…

Manthan, Codefest 19 (open for everyone, rated, Div. 1 + Div. 2) F. Bits And Pieces sosdp预处理超集

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; n≤1e6,ai≤2e6n\le1e6,a_i\le2e6n≤1e6,ai​≤2e6 思路&#xff1a; 由于(aj&ak)(a_j \And a_k)(aj​&ak​)打的括号&#xff0c;所以应该放在一起考虑&#xff0c;现在我们可以枚举aia_iai​&…

架构杂谈《一》

架构杂谈《一》从传统单体架构到服务化架构的发展历程典型的单体架构分为三个层级&#xff0c;Web层、业务逻辑层和数据存储层&#xff0c;每个层的指责分别如下&#xff1a;Web 层&#xff1a;负责与用户交互或者对外提供接口业务逻辑层&#xff1a;为了实现业务逻辑而设计的流…

hdu 3308 LCIS 线段树 + 区间合并

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 日常水一篇题解。 带修改的求区间连续的递增序列&#xff0c;我们考虑用线段树维护。 直接维护mlenmlenmlen是区间最长的递增序列&#xff0c;lslsls是从左端点开始的最长递增序列&#xf…

从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板

标题&#xff1a;从零开始实现ASP.NET Core MVC的插件式开发(二) - 如何创建项目模板作者&#xff1a;Lamond Lu地址&#xff1a;https://www.cnblogs.com/lwqlun/p/11155666.html源代码&#xff1a;https://github.com/lamondlu/DynamicPlugins在上一篇中&#xff0c;我们介绍…

Codeforces Round #112 (Div. 2) E. Compatible Numbers sosdp

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 以下假设all1<<22all1<<22all1<<22。 转化问题&#xff0c;对于每个aia_iai​我们都计算xaixorallxa_i\ \ xor \ \ allxai​ xor all&#xff0c;对于所有xxx的子集都…

多租户通用权限设计(基于 casbin)

所谓权限控制, 概念并不复杂, 就是确认某个操作是否能做, 本质上仅仅就是个bool判断.权限几乎是每个系统必不可少的功能, 和具体业务结合之后, 在系统中往往表现的非常复杂和难于控制, 很大部分原因是把权限和具体业务结合的太过紧密, 把业务的复杂度也加入到权限控制中来了.一…

容器化单页面应用中RESTful API的访问

最近在工作中&#xff0c;需要让运行在容器中的单页面应用程序能够访问外部的RESTful API。这个需求看起来并不困难&#xff0c;不过实现起来还是有些曲折的。在此&#xff0c;我就将这部分内容总结一下。在入正题之前&#xff0c;有个一问题&#xff0c;就是为什么要将单页面应…

LogoSharp:Logo语言的C#实现

上周在朋友中问了一圈&#xff0c;发现没有几个人知道Logo语言的&#xff0c;或许&#xff0c;这门古老的语言现在用的人已经非常少了&#xff0c;除了国外有少量的用户将Logo语言用于教育外&#xff0c;估计国内不会有人使用这门语言。其实&#xff0c;Logo语言本来也就是以编…

Hello Kubernetes快速交互实验手册

“ K8S在线实验室提供了一个交互实验环境&#xff0c;现将这部分文章精简并翻译为中文&#xff0c;希望对那些不了解K8S的童鞋有帮助&#xff0c;能够快速玩起来有个感性认识...”原文&#xff1a;https://kubernetes.io/docs/tutorials翻译&#xff1a;Edison ZhouHello Kuber…

2020 ICPC 上海 Sum of Log 数位dp + 状态剪枝

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 观察可以发现&#xff0c;由于i&j0i \And j0i&j0&#xff0c;所以log2(ij)log_2(ij)log2​(ij)表示的应该是ijijij的111的最高位。 一个显然的dpdpdp状态就出来了f[pos][flag1][fl…

从MVP到微软产品经理的几点心得

昨天&#xff0c;我应邀在微软2019年暑期实习生的入职培训活动上做了一个简短的分享 —— 以我自身的职业发展过程为例&#xff0c;给大家讲了一讲我是怎样从一个技术爱好者&#xff0c;到获得第一个MVP&#xff08;微软最有价值专家&#xff09;的荣誉奖项&#xff0c;以及后来…

小白开学Asp.Net Core 《六》 —— 探究.Net Core 跨平台的奥秘

1、写这篇文章的初衷有好多朋友反馈看不懂我写的开源的一个练手项目&#xff08;GitHub&#xff1a;https://github.com/AjuPrince/Aju.Carefree&#xff09;也有好多人都希望我能写一些简单的入门的文章&#xff0c;记得前几天在群里有人问为什么 .Net Core 能跨平台&#xff…

Codeforces Round #636 (Div. 3) D. Constant Palindrome Sum 思维 + 差分

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; 思路&#xff1a; 首先有一个显然的性质就是每组操作最多不会超过两次。 很容易想到一个很暴力的思路&#xff0c;就是枚举x∈[1,2∗k]x \in [1,2*k]x∈[1,2∗k]&#xff0c;让后判断一下每组需要操作几次取…

C#8.0: 在 LINQ 中支持异步的 IAsyncEnumerableT接口

C# 8.0中&#xff0c;提供了一种新的IAsyncEnumerable<T>接口&#xff0c;在对集合进行迭代时&#xff0c;支持异步操作。比如在读取文本中的多行字符串时&#xff0c;如果读取每行字符串的时候使用同步方法&#xff0c;那么会导致线程堵塞。IAsyncEnumerable<T>可…

Codeforces Round #636 (Div. 3) E. Weights Distributing 思维 + bfs

传送门 文章目录题意&#xff1a;思路&#xff1a;题意&#xff1a; n≤2e5,m≤2e5n\le2e5,m\le2e5n≤2e5,m≤2e5 思路&#xff1a; 怎么感觉每场div3div3div3都有一个巧妙的图论题。 首先如果只有两个点的话&#xff0c;肯定是一次bfsbfsbfs之后取前disdisdis小的www作为边权…

解读 Microsoft.NET.Sdk 的源码,你能定制各种奇怪而富有创意的编译过程

在 csproj 中&#xff0c;Project 中的 Sdk 属性是 MSBuild 15.0 开始支持的&#xff0c;也就是 Visual Studio 2017 开始支持。有了 Sdk 属性的存在&#xff0c;MSBuild 编译过程能够扩展得非常强大&#xff0c;而不止是过去 Import 的一个 props 和 targets 文件。本文将介绍…

《ASP.NET Core 微服务实战》译者序

最近&#xff0c;我将《ASP.NET Core 微服务实战》一书由英文翻译为中文。这本书是由清华大学出版社引进的&#xff0c;目前还处于最后的排版校对过程中&#xff0c;现将该书的译者序发表于此。以下为译者译全文&#xff1a;“微服务”的概念在 2014 年正式提出之后&#xff0c…