典当行网站策划,页面网站缓存如何做,现在那个网站做推广效果会好点,ip可以用wordpress一、什么是循环引用循环引用就是类型相互依赖1. 比如A类有B类的属性,B类也有A类的属性这有什么问题呢?编写生成A的代码需要遍历A的所有属性构造B类型属性是A代码的一部分,B代码又含有A类型属性这就是一个编译死循环2. 其他循环引用的例子链表结构只有一个类型也是类型循环引用…一、什么是循环引用循环引用就是类型相互依赖1. 比如A类有B类的属性,B类也有A类的属性这有什么问题呢?编写生成A的代码需要遍历A的所有属性构造B类型属性是A代码的一部分,B代码又含有A类型属性这就是一个编译死循环2. 其他循环引用的例子链表结构只有一个类型也是类型循环引用A-B-C-A等更长的引用链条也会构成类型循环引用二、举个树状结构的Case树状结构在实际应用中很常见1. 导航菜单代码导航菜单是一个典型的树状结构public class Menu{public int Id { get; set; }public string Name { get; set; }public string Description { get; set; }public ListMenu Children { get; set; }public static Menu GetMenu(){var programs new Menu { Id 2, Name Programs, Description 程序 };var documents new Menu { Id 3, Name Documents, Description 文档 };var settings new Menu { Id 4, Name Settings, Description 设置 };var help new Menu { Id 5, Name Help, Description 帮助 };var run new Menu { Id 6, Name Run, Description 运行 };var shutdown new Menu { Id 7, Name Shut Down, Description 关闭 };var start new Menu { Id 1, Name Start, Description 开始, Children [programs, documents, settings, help, run, shutdown] };return start;}}2. 把Menu转化为MenuDTO2.1 PocoEmit执行代码代码中多加了UseCollection如果全局开启了集合就不需要这行代码var menu Menu.GetMenu();var mapper PocoEmit.Mapper.Create().UseCollection();var dto mapper.ConvertMenu, MenuDTO(menu);2.2 执行效果如下:以下测试是使用vscode执行的(需要Jupyter Notebook插件)测试代码地址为: https://github.com/donetsoftwork/MyEmit/tree/main/Notes/menu.dibgitee地址: https://gitee.com/donetsoftwork/MyEmit/tree/main/Notes/menu.dib{$id: 1,Id: 1,Name: Start,Description: \u5F00\u59CB,Children: {$id: 2,$values: [{$id: 3,Id: 2,Name: Programs,Description: \u7A0B\u5E8F,Children: null},{$id: 4,Id: 3,Name: Documents,Description: \u6587\u6863,Children: null},{$id: 5,Id: 4,Name: Settings,Description: \u8BBE\u7F6E,Children: null},{$id: 6,Id: 5,Name: Help,Description: \u5E2E\u52A9,Children: null},{$id: 7,Id: 6,Name: Run,Description: \u8FD0\u884C,Children: null},{$id: 8,Id: 7,Name: Shut Down,Description: \u5173\u95ED,Children: null}]}}3. 与AutoMapper性能对比如下Method Mean Error StdDev Median Ratio RatioSD Gen0 Gen1 Allocated Alloc RatioAuto 320.14 ns 0.420 ns 0.484 ns 320.10 ns 5.51 0.10 0.0751 0.0003 1296 B 2.95AutoFunc 289.80 ns 6.580 ns 7.313 ns 295.77 ns 4.98 0.15 0.0751 0.0003 1296 B 2.95Poco 58.17 ns 1.031 ns 1.103 ns 58.17 ns 1.00 0.03 0.0255 - 440 B 1.00PocoFunc 48.10 ns 1.059 ns 1.087 ns 49.06 ns 0.83 0.02 0.0255 - 440 B 1.00AutoMapper耗时是Poco的5倍多AutoMapper内存是Poco的近3倍哪怕是用上AutoMapper内部生成的委托也挽救不了多少局面4. 我们增加无循环引用再测试一下4.1 无循环引用菜单public class Menu0{public int ParentId { get; set; }public int Id { get; set; }public string Name { get; set; }public string Description { get; set; }public static ListMenu0 GetMenus(){var start new Menu0 { Id 1, Name Start, Description 开始, ParentId 0 };var programs new Menu0 { Id 2, Name Programs, Description 程序, ParentId 1 };var documents new Menu0 { Id 3, Name Documents, Description 文档, ParentId 1 };var settings new Menu0 { Id 4, Name Settings, Description 设置, ParentId 1 };var help new Menu0 { Id 5, Name Help, Description 帮助, ParentId 1 };var run new Menu0 { Id 6, Name Run, Description 运行 , ParentId 1 };var shutdown new Menu0 { Id 7, Name Shut Down, Description 关闭, ParentId 1 };return [start, programs, documents, settings, help, run, shutdown];}}4.2 性能测试如下Method Mean Error StdDev Median Ratio RatioSD Gen0 Gen1 Allocated Alloc RatioAuto 320.14 ns 0.420 ns 0.484 ns 320.10 ns 5.51 0.10 0.0751 0.0003 1296 B 2.95Auto0 110.60 ns 1.130 ns 1.302 ns 110.30 ns 1.90 0.04 0.0264 - 456 B 1.04AutoFunc 289.80 ns 6.580 ns 7.313 ns 295.77 ns 4.98 0.15 0.0751 0.0003 1296 B 2.95Poco 58.17 ns 1.031 ns 1.103 ns 58.17 ns 1.00 0.03 0.0255 - 440 B 1.00Poco0 60.80 ns 0.176 ns 0.202 ns 60.73 ns 1.05 0.02 0.0227 - 392 B 0.89PocoFunc 48.10 ns 1.059 ns 1.087 ns 49.06 ns 0.83 0.02 0.0255 - 440 B 1.00Auto0是AutoMapper把Menu0列表转化为DTO的casePoco0是Poco把Menu0列表转化为DTO的caseAutoMapper循环引用处理耗时和内存都是列表的3倍Poco循环引用处理和列表性能差不多当然就算是无循环引用的列表处理,AutoMapper耗时也几乎是Poco的两倍这充分说明AutoMapper处理循环引用是有问题的5. 先对比一下AutoMapper有无循环引用的代码5.1 AutoMapper无循环引用的代码如下T __fT(System.FuncT f) f();(FuncListMenu0, ListMenu0DTO, ResolutionContext, ListMenu0DTO)((ListMenu0 source,ListMenu0DTO mapperDestination,ResolutionContext context) //ListMenu0DTO(source null) ?new ListMenu0DTO() :__f(() {try{ListMenu0DTO collectionDestination null;ListMenu0DTO passedDestination null;passedDestination mapperDestination;collectionDestination passedDestination ?? new ListMenu0DTO();collectionDestination.Clear();ListMenu0.Enumerator enumerator default;Menu0 item null;enumerator source.GetEnumerator();try{while (true){if (enumerator.MoveNext()){item enumerator.Current;collectionDestination.Add(((FuncMenu0, Menu0DTO, ResolutionContext, Menu0DTO)((Menu0 source_1,Menu0DTO destination,ResolutionContext context) //Menu0DTO(source_1 null) ?(destination null) ? (Menu0DTO)null : destination :__f(() {Menu0DTO typeMapDestination null;typeMapDestination destination ?? new Menu0DTO();try{typeMapDestination.ParentId source_1.ParentId;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}try{typeMapDestination.Id source_1.Id;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}try{typeMapDestination.Name source_1.Name;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}try{typeMapDestination.Description source_1.Description;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}return typeMapDestination;}))).Invoke(item,(Menu0DTO)null,context));}else{goto LoopBreak;}}LoopBreak:;}finally{enumerator.Dispose();}return collectionDestination;}catch (Exception ex){throw MapperConfiguration.GetMappingError(ex,default(MapRequest)/*NOTE: Provide the non-default value for the Constant!*/);}}));5.2 AutoMapper循环引用的代码如下5.2.1 Menu转MenuDTOT __fT(System.FuncT f) f();(FuncMenu, MenuDTO, ResolutionContext, MenuDTO)((Menu source,MenuDTO destination,ResolutionContext context) //MenuDTO(source null) ?(destination null) ? (MenuDTO)null : destination :__f(() {MenuDTO typeMapDestination null;ResolutionContext.CheckContext(ref context);return ((MenuDTO)context.GetDestination(source,typeof(MenuDTO))) ??__f(() {typeMapDestination destination ?? new MenuDTO();context.CacheDestination(source,typeof(MenuDTO),typeMapDestination);typeMapDestination;try{typeMapDestination.Id source.Id;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}try{typeMapDestination.Name source.Name;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}try{typeMapDestination.Description source.Description;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}try{ListMenu resolvedValue null;ListMenuDTO mappedValue null;resolvedValue source.Children;mappedValue (resolvedValue null) ?new ListMenuDTO() :context.MapInternalListMenu, ListMenuDTO(resolvedValue,(destination null) ? (ListMenuDTO)null :typeMapDestination.Children,(MemberMap)default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);typeMapDestination.Children mappedValue;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}return typeMapDestination;});}));5.2.2 ListMenu转ListMenuDTOT __fT(System.FuncT f) f();(FuncListMenu, ListMenuDTO, ResolutionContext, ListMenuDTO)((ListMenu source,ListMenuDTO mapperDestination,ResolutionContext context) //ListMenuDTO(source null) ?new ListMenuDTO() :__f(() {try{ListMenuDTO collectionDestination null;ListMenuDTO passedDestination null;ResolutionContext.CheckContext(ref context);passedDestination mapperDestination;collectionDestination passedDestination ?? new ListMenuDTO();collectionDestination.Clear();ListMenu.Enumerator enumerator default;Menu item null;enumerator source.GetEnumerator();try{while (true){if (enumerator.MoveNext()){item enumerator.Current;collectionDestination.Add(((FuncMenu, MenuDTO, ResolutionContext, MenuDTO)((Menu source_1,MenuDTO destination,ResolutionContext context) //MenuDTO(source_1 null) ?(destination null) ? (MenuDTO)null : destination :__f(() {MenuDTO typeMapDestination null;ResolutionContext.CheckContext(ref context);return ((MenuDTO)context.GetDestination(source_1,typeof(MenuDTO))) ??__f(() {typeMapDestination destination ?? new MenuDTO();context.CacheDestination(source_1,typeof(MenuDTO),typeMapDestination);typeMapDestination;try{typeMapDestination.Id source_1.Id;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}try{typeMapDestination.Name source_1.Name;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}try{typeMapDestination.Description source_1.Description;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}try{ListMenu resolvedValue null;ListMenuDTO mappedValue null;resolvedValue source_1.Children;mappedValue (resolvedValue null) ?new ListMenuDTO() :context.MapInternalListMenu, ListMenuDTO(resolvedValue,(destination null) ? (ListMenuDTO)null :typeMapDestination.Children,(MemberMap)default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);typeMapDestination.Children mappedValue;}catch (Exception ex){throw TypeMapPlanBuilder.MemberMappingError(ex,default(PropertyMap)/*NOTE: Provide the non-default value for the Constant!*/);}return typeMapDestination;});}))).Invoke(item,(MenuDTO)null,context));}else{goto LoopBreak;}}LoopBreak:;}finally{enumerator.Dispose();}return collectionDestination;}catch (Exception ex){throw MapperConfiguration.GetMappingError(ex,default(MapRequest)/*NOTE: Provide the non-default value for the Constant!*/);}}));5.3 AutoMapper有无循环引用的代码分析如下循环引用的代码有2段,1段处理Menu,另1段处理ListMenu直接对比处理ListMenu部分很明显有循环引用部分多了不少特殊代码5.3.1 AutoMapper循环引用多出以下代码ResolutionContext.CheckContext消耗内存context.GetDestination消耗内存和cpucontext.CacheDestination消耗内存和cpucontext.MapInternal用于调用代码5.3.2 AutoMapper代码总结MapInternal用于解决编译死循环的问题GetDestination和CacheDestination用于解决执行死循环的问题但是这个case没有对象重复引用,没有执行死循环也就是说这里的GetDestination和CacheDestination只是消耗内存和cpu做无用功更让人无法接受的是,做这些无用功的消耗居然是正常代码的好几倍在无循环引用代码中ResolutionContext就是个摆设,无任何作用6. 执行死循环该怎么处理呢.net序列化给了我们答案序列化默认不支持对象循环引用,需要特殊配置,这是为了照顾大部分情况下的性能6.1 序列化对象循环引用代码Node node9 new() { Id 9, Name node9 };Node node8 new() { Id 8, Name node8, Next node9 };Node node7 new() { Id 7, Name node7, Next node8 };Node node6 new() { Id 6, Name node6, Next node7 };Node node5 new() { Id 5, Name node5, Next node6 };Node node4 new() { Id 4, Name node4, Next node5 };Node node3 new() { Id 3, Name node3, Next node4 };Node node2 new() { Id 2, Name node2, Next node3 };Node node1 new() { Id 1, Name node1, Next node2 };node9.Next node1; // 形成环var referenceJson JsonSerializer.Serialize(dto, new JsonSerializerOptions{ReferenceHandler ReferenceHandler.Preserve,WriteIndented true});referenceJson.Display();如果以上代码不配置ReferenceHandler会报错异常信息为A possible object cycle was detected...7. Poco循环引用处理的代码7.1 Menu转DTO代码如下(FuncMenu, MenuDTO)((Menu source) //MenuDTO{MenuDTO dest null;if ((source ! (Menu)null)){dest new MenuDTO();ListMenu Children null;dest.Id source.Id;dest.Name source.Name;dest.Description source.Description;Children source.Children;if ((Children ! null)){dest.Children default(CompiledConverterListMenu, ListMenuDTO)/*NOTE: Provide the non-default value for the Constant!*/.Convert(Children);}}return dest;});7.2 ListMenu转DTO代码如下(FuncListMenu, ListMenuDTO)((ListMenu source) //ListMenuDTO{ListMenuDTO dest null;if ((source ! (ListMenu)null)){dest new ListMenuDTO(source.Count);int index default;int len default;index 0;len source.Count;while (true){if ((index len)){Menu sourceItem null;MenuDTO destItem null;sourceItem source[index];// { The block result will be assigned to destItemMenuDTO dest_1 null;destItem ((FuncMenu, MenuDTO)((Menu source_1) //MenuDTO{MenuDTO dest_2 null;if ((source_1 ! (Menu)null)){dest_2 new MenuDTO();ListMenu Children null;dest_2.Id source_1.Id;dest_2.Name source_1.Name;dest_2.Description source_1.Description;Children source_1.Children;if ((Children ! null)){dest_2.Children default(CompiledConverterListMenu, ListMenuDTO)/*NOTE: Provide the non-default value for the Constant!*/.Convert(Children);}}return dest_2;})).Invoke(sourceItem);// } end of block assignment;dest.Add(destItem);index;}else{goto forLabel;}}forLabel:;}return dest;});8. AutoMapper和Poco生成代码对比AutoMapper生成代码量是Poco的3倍多AutoMapper生成的代码可读性不好,Poco生成的代码几乎就是正常程序员手写代码CompiledConverter.Convert对应AutoMapper的context.MapInternal本case中Poco无多余缓存处理,节省了大量cpu和内存如果有对象循环引用Poco该怎么办呢三、再举个环形链表的Case链表是类型循环引用环形链表又是对象循环引用中国传统有九九归一的说法,以此为例1. 九九归一代码public class Node{public int Id { get; set; }public string Name { get; set; }public Node Next { get; set; }public static Node GetNode(){Node node9 new() { Id 9, Name node9 };Node node8 new() { Id 8, Name node8, Next node9 };Node node7 new() { Id 7, Name node7, Next node8 };Node node6 new() { Id 6, Name node6, Next node7 };Node node5 new() { Id 5, Name node5, Next node6 };Node node4 new() { Id 4, Name node4, Next node5 };Node node3 new() { Id 3, Name node3, Next node4 };Node node2 new() { Id 2, Name node2, Next node3 };Node node1 new() { Id 1, Name node1, Next node2 };node9.Next node1; // 形成环return node1;}}