{"id":42006144,"url":"https://github.com/ldqk0/Masuit.Tools","last_synced_at":"2026-02-05T01:00:48.990Z","repository":{"id":37628748,"uuid":"159758636","full_name":"ldqk0/Masuit.Tools","owner":"ldqk0","description":"该仓库为 https://github.com/ldqk/Masuit.Tools 的镜像仓库，代码更新存在较大的延迟。建议前往源仓库：https://github.com/ldqk/Masuit.Tools","archived":false,"fork":false,"pushed_at":"2025-07-02T01:34:01.000Z","size":9114,"stargazers_count":802,"open_issues_count":0,"forks_count":232,"subscribers_count":33,"default_branch":"master","last_synced_at":"2025-07-02T02:31:29.095Z","etag":null,"topics":["csharp","linq","masuit"],"latest_commit_sha":null,"homepage":"https://github.com/ldqk/Masuit.Tools","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ldqk0.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2018-11-30T02:43:45.000Z","updated_at":"2025-07-02T01:34:13.000Z","dependencies_parsed_at":"2024-07-16T08:55:26.266Z","dependency_job_id":null,"html_url":"https://github.com/ldqk0/Masuit.Tools","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/ldqk0/Masuit.Tools","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ldqk0%2FMasuit.Tools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ldqk0%2FMasuit.Tools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ldqk0%2FMasuit.Tools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ldqk0%2FMasuit.Tools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ldqk0","download_url":"https://codeload.github.com/ldqk0/Masuit.Tools/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ldqk0%2FMasuit.Tools/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29105268,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-05T00:52:08.035Z","status":"ssl_error","status_checked_at":"2026-02-05T00:52:07.703Z","response_time":62,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","linq","masuit"],"created_at":"2026-01-26T02:00:36.132Z","updated_at":"2026-02-05T01:00:48.975Z","avatar_url":"https://github.com/ldqk0.png","language":"C#","funding_links":[],"categories":["C# #"],"sub_categories":[],"readme":"﻿# Masuit.Tools(码数吐司库)\n\n[![许可证](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/ldqk/Masuit.Tools/blob/master/LICENSE)\n[![nuget](https://img.shields.io/nuget/v/Masuit.Tools.Core.svg)](https://www.nuget.org/packages/Masuit.Tools.Core)\n[![nuget](https://img.shields.io/nuget/dt/Masuit.Tools.Core.svg)](https://www.nuget.org/packages/Masuit.Tools.Core)\n![codeSize](https://img.shields.io/github/languages/code-size/ldqk/Masuit.Tools.svg)\n![编程语言](https://img.shields.io/github/languages/top/ldqk/Masuit.Tools.svg)\n\u003ca href=\"https://gitee.com/masuit/Masuit.Tools\"\u003e\u003cimg src=\"https://gitee.com/static/images/logo-black.svg\" height=\"24\"\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/ldqk/Masuit.Tools\"\u003e\u003cimg src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/9/95/Font_Awesome_5_brands_github.svg/54px-Font_Awesome_5_brands_github.svg.png\" height=\"24\"\u003e\u003cimg src=\"https://upload.wikimedia.org/wikipedia/commons/thumb/2/29/GitHub_logo_2013.svg/128px-GitHub_logo_2013.svg.png\" height=\"24\"\u003e\u003c/a\u003e\n\n全龄段友好的C#.NET万能工具库，不管你是菜鸟新手还是骨灰级玩家都能轻松上手，这个库包含一些常用的操作类，大都是静态类，加密解密，反射操作，树结构，文件探测，权重随机筛选算法，分布式短id，表达式树，linq扩展，文件压缩，多线程下载，硬件信息，字符串扩展方法，日期时间扩展操作，中国农历，大文件拷贝，图像裁剪，验证码，断点续传，集合扩展、Excel导出等常用封装。\n\n**诸多功能集一身，代码量不到2MB！**\n[官网教程](https://www.masuit.tools)\n![Masuit Tools](https://github.com/user-attachments/assets/ea9fb2f2-6095-46e3-bd4a-e18c99a743df)\n\n项目开发模式：日常代码积累+网络搜集\n\n⭐⭐⭐喜欢这个项目的话就Star、Fork、Follow素质三连关♂注一下吧⭐⭐⭐\n\n关于本项目，如果你有任何不懂的地方或使用过程中遇到任何问题，可以直接提issue或私信联系我，我会为你提供**完全免费**的技术指导，当然，如果你觉得不好意思接受免费的指导，想适当打赏我也是不会拒绝的！🤣🤣🤣\n\n## 本项目已得到[JetBrains](https://www.jetbrains.com/shop/eform/opensource)的支持！\n\n\u003cimg src=\"https://www.jetbrains.com/shop/static/images/jetbrains-logo-inv.svg\" height=\"100\"\u003e\n\n## Star趋势\n\n\u003cimg src=\"https://starchart.cc/ldqk/Masuit.Tools.svg\"\u003e\n\n## 请注意：\n\n一旦使用本开源项目以及引用了本项目或包含本项目代码的公司因为违反劳动法（包括但不限定非法裁员、超时用工、雇佣童工等）在任何法律诉讼中败诉的，一经发现，本项目作者有权利追讨本项目的使用费（**公司工商注册信息认缴金额的2-5倍作为本项目的授权费**），或者直接不允许使用任何包含本项目的源代码！ `人力外包公司`或 `007公司`需要使用本类库，请联系作者进行商业授权！其他企业或个人可随意使用不受限。007那叫用人，也是废人。8小时工作制才可以让你有时间自我提升，将来有竞争力。反对007，人人有责！\n\n## 建议开发环境\n\n操作系统：Windows 11 23H2及以上版本\n\n开发工具：VisualStudio2022 v17.8及以上版本\n\nSDK：.Net Core 2.1.0及以上**所有版本**\n\n## 安装程序包\n\n### 基础功能包\n#### .NET Framework ≥ 4.6.2\n\n```shell\nPM\u003e Install-Package Masuit.Tools.Net\n```\n\n#### .NET Standard ≥ 2.1 或只想使用一些基本功能\n\n`通用项目推荐首选包`\n\n```shell\nPM\u003e Install-Package Masuit.Tools.Abstraction\n```\n\n#### .NET Core ≥ 2.1\n\n`.NET Core项目推荐首选包`\n\n```shell\nPM\u003e Install-Package Masuit.Tools.Core\n```\n\n#### .NET Framework 4.5特供版\n\n请注意：`这是.NET Framework 4.5的专用版本，相比4.6.2及.NET Core的版本，阉割了Redis、HTML、文件压缩、ASP.NET扩展、硬件监测、Session扩展等一些功能。`**如果你的项目版本高于4.6.2，请务必使用上述版本的包，以享受完整的功能体验！**\n\n```shell\nPM\u003e Install-Package Masuit.Tools.Net45\n```\n\n### 增值包\n\n#### Masuit.Tools.AspNetCore\n\n`AspNetCore项目推荐首选包`\nASP.NET Core Web专用包，包含Masuit.Tools.Core的全部功能，并且增加了一些对ASP.NET Core Web功能的额外支持。\n\n#### Masuit.Tools.Excel\n\nExcel导入导出的专用独立包\n\n#### Masuit.Tools.NoSQL.MongoDBClient\n\nmongodb的封装操作类独立包\n\n## 为工具库注册配置(可选的，按需配置)\n\n工具库需要用到外部配置节，.NET Framework项目配置在web.config/app.config的AppSettings配置节中，.NET Core项目配置在appsettings.json中：\n\n1. EmailDomainWhiteList，邮箱校验需要用到的白名单域名，英文逗号分隔，每个元素支持正则表达式，若未配置，则不启用邮箱校验白名单，示例: `\"^\\\\w{1,5}@qq.com,^\\\\w{1,5}@163.com,^\\\\w{1,5}@gmail.com,^\\\\w{1,5}@outlook.com\"`\n2. EmailDomainBlockList，邮箱校验需要用到的黑名单域名，英文逗号分隔，每个元素支持正则表达式，且黑名单优先级高于白名单，若未配置，则不启用邮箱校验黑白名单\n\n```csharp\npublic Startup(IConfiguration configuration)\n{\n    configuration.AddToMasuitTools(); // 若未调用，则默认自动尝试加载appsettings.json\n}\n```\n\n## 特色功能示例代码\n\n### 在线体验\n\nhttps://replit.com/@ldqk/MasuitToolsDemo?v=1#main.cs\n\n### 0. 一些创意类型\n\n`DisposableDictionary`：可被Disposable的字典类型，用于存放Value是Disposable类型的数据，用法和普通字典一致\n\n`NullableConcurrentDictionary`/`NullableDictionary`：Key可为null的字典类型，用法和普通字典一致\n\n`ConcurrentHashSet`：并发HashSet，用法和HashSet一致\n\n`ConcurrentLimitedQueue`：定长并发队列，特点是长度是固定的，用法与ConcurrentQueue一致\n\n`LimitedQueue`：定长队列，特点是长度是固定的，用法与Queue一致\n\n`LargeMemoryStream`：超大内存流，最大可支持1TB数据，推荐当数据流大于2GB时使用，用法与MemoryStream一致\n\n`PooledMemoryStream`：池化内存流，可内存复用，用法与MemoryStream一致，性能比MemoryStream好\n\n`ITree\u003cT\u003e`：树形实体接口约束，实现该接口可让类型实现一些树形操作\n\n`ChineseCalendar`：中国农历类型，可以实现天干地支节气等数据的获取\n\n`Clay`/`DynamicFactory`：粘土动态类型，可实现类似js的弱类型编程\n\n`RadarChart`：雷达图类型，可用于做数据分析或用户行为画像\n\n`Circle`：圆形类型，可实现⚪的相交相切相离的判断\n\n`Sphere`：球体类型，可实现计算球体上两点的弧长计算，相交相切相离的判断\n\n`MimeMapper`：mime类型映射\n\n`VersionNumber`：版本号类型，比System.Version功能更多一点，用法一致\n\n具体用法，可参阅后文详细示例：\n\n### 1. 检验字符串是否是Email、手机号、URL、IP地址、身份证号等\n\n```csharp\nvar (isMatch, match) = \"337845818@qq.com\".MatchEmail(); // 可在appsetting.json中添加EmailDomainWhiteList和EmailDomainBlockList配置邮箱域名黑白名单，逗号分隔，如\"EmailDomainBlockList\": \"^\\\\w{1,5}@qq.com,^\\\\w{1,5}@163.com,^\\\\w{1,5}@gmail.com,^\\\\w{1,5}@outlook.com\",\nbool isInetAddress = \"114.114.114.114\".MatchInetAddress(); // 匹配IP地址\nbool isUrl = \"http://masuit.org/20/history\".MatchUrl(); // 匹配url\nbool isPhoneNumber = \"15205201520\".MatchPhoneNumber(); // 匹配手机号\nbool isLandline = \"01088888888\".MatchLandline(); // 匹配座机号\nbool isIdentifyCard = \"312000199502230660\".MatchIdentifyCard();// 校验中国大陆身份证号\nbool isCNPatentNumber = \"200410018477.9\".MatchCNPatentNumber(); // 校验中国专利申请号或专利号，是否带校验位，校验位前是否带“.”，都可以校验，待校验的号码前不要带CN、ZL字样的前缀\nbool isUSCC = \"200410018477.9\".MatchUSCC(); // 校验企业统一社会信用代码\n```\n\n### 2.硬件监测(需要管理员权限，仅支持Windows，部分函数仅支持物理机模式)\n\n```csharp\nfloat load = SystemInfo.CpuLoad;// 获取CPU占用率\nlong physicalMemory = SystemInfo.PhysicalMemory;// 获取物理内存总数\nlong memoryAvailable = SystemInfo.MemoryAvailable;// 获取物理内存可用率\ndouble freePhysicalMemory = SystemInfo.GetFreePhysicalMemory();// 获取可用物理内存\ndouble temperature = SystemInfo.GetCPUTemperature();// 获取CPU温度\nint cpuCount = SystemInfo.GetCpuCount();// 获取CPU核心数\nvar ipAddress = SystemInfo.GetLocalIPs();// 获取本机所有IP地址\nstring localUsedIp = SystemInfo.GetLocalUsedIP();// 获取本机当前正在使用的IP地址\nIList\u003cstring\u003e macAddress = SystemInfo.GetMacAddress();// 获取本机所有网卡mac地址\nstring osVersion = Windows.GetOsVersion();// 获取操作系统版本\nRamInfo ramInfo = SystemInfo.GetRamInfo();// 获取内存信息\nvar cpuSN=SystemInfo.GetCpuInfo()[0].SerialNumber; // CPU序列号\nvar driveSN=SystemInfo.GetDiskInfo()[0].SerialNumber; // 硬盘序列号\n\n// 快速方法\nvar cpuInfos = CpuInfo.Locals; // 快速获取CPU的信息\nvar ramInfo = RamInfo.Local; // 快速获取内存的信息\nvar diskInfos = DiskInfo.Locals; // 快速获取硬盘的信息\nvar biosInfo = BiosInfo.Local; // 快速获取主板的信息\n\n// 获取进程的CPU和内存占用率\nvar process = Process.GetProcessById(1234); // pid获取\nvar cpuUsage = process.GetProcessCpuUsage();\nvar memory = process.GetProcessMemory();\n\nvar processes = Process.GetProcessesByName(\"msedge\"); // 进程名获取\nforeach (var p in processes)\n{\n    var cpu = p.GetProcessCpuUsage();\n    var mem = p.GetProcessMemory();\n    Console.WriteLine($\"Process {p.ProcessName} CPU: {cpu}%, Memory: {mem}MB\");\n}\n```\n\n### 3.html的防XSS处理：\n\n```csharp\nstring html = @\"\u003clink href='/Content/font-awesome/css' rel='stylesheet'/\u003e\n        \u003c!--[if IE 7]\u003e\n        \u003clink href='/Content/font-awesome-ie7.min.css' rel='stylesheet'/\u003e\n        \u003c![endif]--\u003e\n        \u003cscript src='/Scripts/modernizr'\u003e\u003c/script\u003e\n        \u003cdiv id='searchBox' role='search'\u003e\n        \u003cform action='/packages' method='get'\u003e\n        \u003cspan class='user-actions'\u003e\u003ca href='/users/account/LogOff'\u003e退出\u003c/a\u003e\u003c/span\u003e\n        \u003cinput name='q' id='searchBoxInput'/\u003e\n        \u003cinput id='searchBoxSubmit' type='submit' value='Submit' /\u003e\n        \u003c/form\u003e\n        \u003c/div\u003e\";\nstring s = html.HtmlSanitizerStandard();// 清理后：\u003cdiv\u003e\u003cspan\u003e\u003ca href=\"/users/account/LogOff\"\u003e退出\u003c/a\u003e\u003c/span\u003e\u003c/div\u003e\nstring s = html.HtmlSanitizerCustom(); // 自定义清理\n```\n\n### 4.整理Windows系统的内存：\n\n类似于各大系统优化软件的加速球功能\n\n```csharp\nWindows.ClearMemorySilent();\n```\n\n### 5.任意进制转换/中文数字\n\n#### 大写数字\n\n```csharp\nvar num=123.45.ToChineseMoney(); // 壹佰贰拾叁元肆角伍分\nvar num=123.45.ToChineseNumber(); // 一百二十三点四五\n```\n\n#### 进制转换\n\n可用于生成短id，短hash，随机字符串等操作，纯数学运算。\n\n```csharp\nNumberFormater nf = new NumberFormater(36);//内置2-94进制的转换\n//NumberFormater nf = new NumberFormater(\"0123456789abcdefghijklmnopqrstuvwxyz\");// 自定义进制字符，可用于生成验证码，自定义字符可支持任意进制，你传1w个字符进去那就支持一万进制(手动狗头)\nstring s36 = nf.ToString(12345678);\nlong num = nf.FromString(\"7clzi\");\nConsole.WriteLine(\"12345678的36进制是：\" + s36); // 7clzi\nConsole.WriteLine(\"36进制的7clzi是：\" + num); // 12345678\nvar s = new NumberFormater(91).ToString(new Random().Next(100000, int.MaxValue)); //配合随机数生成随机字符串\n```\n\n```csharp\n//扩展方法形式调用\nvar bin=12345678.ToBase(36);// 10进制转36进制：7clzi\nvar num=\"7clzi\".FromBase(36);// 36进制转10进制：12345678\n```\n\n```csharp\n//超大数字的进制转换\nvar num = \"e6186159d38cd50e0463a55e596336bd\".FromBaseBig(16); // 大数字16进制转10进制\nConsole.WriteLine(num); // 十进制：305849028665645097422198928560410015421\nConsole.WriteLine(num.ToBase(64)); // 64进制：3C665pQUPl3whzFlVpoPqZ，22位长度\nConsole.WriteLine(num.ToBase(36)); // 36进制：dmed4dkd5bhcg4qdktklun0zh，25位长度\nConsole.WriteLine(num.ToBase(7)); // 7进制：2600240311641665565300424545154525131265221035，46位长度\nConsole.WriteLine(num.ToBase(12)); // 12进制：5217744842749978a756b22135b16a5998a5，36位长度\nConsole.WriteLine(num.ToBase(41)); // 41进制：opzeBda2aytcEeudEquuesbk，24位长度\n```\n\n如果你想让进制符支持emoji，NumberFormater是不支持的，不过如果你确实有这么骚的需求，我还准备了UnicodeFormater类，用于支持emoji，用法和NumberFormater一模一样，并且，UnicodeFormater的功能包含NumberFormater的功能，但是，性能比NumberFormater差了许多。\n\n```csharp\nvar formater = new UnicodeFormater(\"😀😁😂🤣😃😄😅😆😉😊😋😎😍😘🥰😗😙🥲😚🙂🤗🤩🤔🤨😑😶😶‍🌫🙄😏😣😥😮\");\nvar s = formater.ToString(1234567890); // 😄🌫😶😋😋\nvar num = formater.FromString(s); // 1234567890\n```\n\n### 6.纳秒级性能计时器\n\n```csharp\nHiPerfTimer timer = HiPerfTimer.StartNew();\nfor (int i = 0; i \u003c 100000; i++)\n{\n    //todo\n}\ntimer.Stop();\nConsole.WriteLine(\"执行for循环100000次耗时\"+timer.Duration+\"s\");\n```\n\n```csharp\ndouble time = HiPerfTimer.Execute(() =\u003e\n{\n    for (int i = 0; i \u003c 100000; i++)\n    {\n        //todo\n    }\n});\nConsole.WriteLine(\"执行for循环100000次耗时\"+time+\"s\");\n```\n\n### 7.产生分布式唯一有序短id(雪花id)\n\n```csharp\n// 实例调用\nvar sf = SnowFlake.GetInstance();\nstring id = sf.GetUniqueId();// rcofqodori0w\nvar sfn = SnowFlakeNew.GetInstance(); // 改良版雪花id，对时间回拨不敏感\nstring id = sfn.GetUniqueId();// vmbq8q3s3zul\n\n// 静态调用\nstring id = SnowFlake.NewId;// rcofqodori0w\nstring shortId = sf.GetUniqueShortId(8);// qodw9728\nstring id = SnowFlakeNew.NewId;// 改良版雪花id，对时间回拨不敏感\n\n// 全局设置\nSnowFlake.SetMachienId(1); // 设置机器id\nSnowFlake.SetInitialOffset(4219864516915105792); // 设置起始偏移量\nSnowFlake.SetNumberFormater(new NumberFormater(\"0123456789abcdefghijklmnopqrstuvwxyz._-!\")); // 设置数制格式化器\n\nSnowFlakeNew.SetMachienId(1); // 设置机器id\nSnowFlakeNew.SetInitialOffset(4219864516915105792); // 设置起始偏移量\nSnowFlakeNew.SetNumberFormater(new NumberFormater(\"0123456789abcdefghijklmnopqrstuvwxyz._-!\")); // 设置数制格式化器\n```\n\n```csharp\nvar set = new HashSet\u003cstring\u003e();\ndouble time = HiPerfTimer.Execute(() =\u003e\n{\n    for (int i = 0; i \u003c 1000000; i++)\n    {\n        set.Add(SnowFlake.NewId);\n    }\n});\nConsole.WriteLine(set.Count == 1000000); //True\nConsole.WriteLine(\"产生100w个id耗时\" + time + \"s\"); //2.6891495s\n```\n\n### 8.农历转换\n\n```csharp\nChineseCalendar.CustomHolidays.Add(DateTime.Parse(\"2018-12-31\"),\"元旦节\");//自定义节假日\nChineseCalendar today = new ChineseCalendar(DateTime.Parse(\"2018-12-31\"));\nConsole.WriteLine(today.ChineseDateString);// 二零一八年十一月廿五\nConsole.WriteLine(today.AnimalString);// 生肖：狗\nConsole.WriteLine(today.GanZhiDateString);// 干支：戊戌年甲子月丁酉日\nConsole.WriteLine(today.DateHoliday);// 获取按公历计算的节假日\n...\n```\n\n### 9.Linq表达式树扩展\n\n```csharp\nExpression\u003cFunc\u003cstring, bool\u003e\u003e where1 = s =\u003e s.StartsWith(\"a\");\nExpression\u003cFunc\u003cstring, bool\u003e\u003e where2 = s =\u003e s.Length \u003e 10;\nFunc\u003cstring, bool\u003e func = where1.And(where2)\n    .AndIf(!string.IsNullOrEmpty(name),s=\u003es==name)\n    .Compile(); // And和AndIf可供选择，满足条件再执行And\nbool b=func(\"abcd12345678\");//true\n```\n\n```csharp\nExpression\u003cFunc\u003cstring, bool\u003e\u003e where1 = s =\u003e s.StartsWith(\"a\");\nExpression\u003cFunc\u003cstring, bool\u003e\u003e where2 = s =\u003e s.Length \u003e 10;\nFunc\u003cstring, bool\u003e func = where1\n    .Or(where2)\n    .OrIf(!string.IsNullOrEmpty(name),s=\u003es==name)\n    .Compile(); // Or和OrIf可供选择，满足条件再执行Or\nbool b=func(\"abc\");// true\n```\n\n```csharp\nqueryable.WhereIf(!string.IsNullOrEmpty(name),e=\u003ee.Name==name)\n    .WhereIf(()=\u003e age.HasValue,e=\u003ee.Age\u003e=age); // IQueryable的WhereIf扩展函数，满足条件再执行Where\n```\n\n### 10.模版引擎\n\n```csharp\nvar tmp = new Template(\"{{name}}，你好！\");\ntmp.Set(\"name\", \"万金油\");\nstring s = tmp.Render();//万金油，你好！\n```\n\n```csharp\nvar tmp = new Template(\"{{one}},{{two}},{{three}}\");\nstring s = tmp.Set(\"one\", \"1\").Set(\"two\", \"2\").Set(\"three\", \"3\").Render();// 1,2,3\n```\n\n```csharp\nvar tmp = new Template(\"{{name}}，{{greet}}！\");\ntmp.Set(\"name\", \"万金油\");\nstring s = tmp.Render();// throw 模版变量{{greet}}未被使用\n```\n\n### 11.List转Datatable\n\n```csharp\nvar list = new List\u003cMyClass\u003e()\n{\n    new MyClass()\n    {\n        Name = \"张三\",\n        Age = 22\n    },\n    new MyClass()\n    {\n        Name = \"李四\",\n        Age = 21\n    },\n    new MyClass()\n    {\n        Name = \"王五\",\n        Age = 28\n    }\n};\nvar table = list.Select(c =\u003e new{姓名=c.Name,年龄=c.Age}).ToDataTable();// 将自动填充列姓名和年龄\n```\n\n### 12.文件压缩解压\n\n.NET Framework\n\n```csharp\nMemoryStream ms = SevenZipCompressor.ZipStream(new List\u003cstring\u003e()\n{\n    @\"D:\\1.txt\",\n    \"http://ww3.sinaimg.cn/large/87c01ec7gy1fsq6rywto2j20je0d3td0.jpg\",\n});//压缩成内存流\n```\n\n```csharp\nSevenZipCompressor.Zip(new List\u003cstring\u003e()\n{\n    @\"D:\\1.txt\",\n    \"http://ww3.sinaimg.cn/large/87c01ec7gy1fsq6rywto2j20je0d3td0.jpg\",\n}, zip);//压缩成zip\nSevenZipCompressor.UnRar(@\"D:\\Download\\test.rar\", @\"D:\\Download\\\");//解压rar\nSevenZipCompressor.Decompress(@\"D:\\Download\\test.tar\", @\"D:\\Download\\\");//自动识别解压压缩包\nSevenZipCompressor.Decompress(@\"D:\\Download\\test.7z\", @\"D:\\Download\\\");\n```\n\nASP.NET Core\n\nStartup.cs\n\n```csharp\nservices.AddSevenZipCompressor();\n```\n\n构造函数注入ISevenZipCompressor\n\n```csharp\nprivate readonly ISevenZipCompressor _sevenZipCompressor;\npublic Test(ISevenZipCompressor sevenZipCompressor)\n{\n    _sevenZipCompressor = sevenZipCompressor;\n}\n```\n\n使用方式同.NET Framework版本\n\n### 13.简易日志组件(又不是不能用.jpg)\n\n```csharp\nLogManager.LogDirectory=AppDomain.CurrentDomain.BaseDirectory+\"/logs\";\nLogManager.Event+=info =\u003e\n{\n    //todo:注册一些事件操作\n};\nLogManager.Info(\"记录一次消息\");\nLogManager.Error(new Exception(\"异常消息\"));\n```\n\n### 14.多线程后台下载\n\n```csharp\nvar mtd = new MultiThreadDownloader(\"https://attachments-cdn.shimo.im/yXwC4kphjVQu06rH/KeyShot_Pro_7.3.37.7z\",Environment.GetEnvironmentVariable(\"temp\"),\"E:\\\\Downloads\\\\KeyShot_Pro_7.3.37.7z\",8);\nmtd.Configure(req =\u003e\n {\n     req.Referer = \"https://masuit.com\";\n     req.Headers.Add(\"Origin\", \"https://baidu.com\");\n});\nmtd.TotalProgressChanged+=(sender, e) =\u003e\n{\n    var downloader = sender as MultiThreadDownloader;\n    Console.WriteLine(\"下载进度：\"+downloader.TotalProgress+\"%\");\n    Console.WriteLine(\"下载速度：\"+downloader.TotalSpeedInBytes/1024/1024+\"MBps\");\n};\nmtd.FileMergeProgressChanged+=(sender, e) =\u003e\n{\n    Console.WriteLine(\"下载完成\");\n};\nmtd.FileMergedComplete+=(sender,e)=\u003e{\n    Console.WriteLine(\"文件合并完成\");\n};\nmtd.Start();//开始下载\n//mtd.Pause(); // 暂停下载\n//mtd.Resume(); // 继续下载\n```\n\n### 15.加密解密/hash\n\n```csharp\nvar enc=\"123456\".MDString();// MD5\nvar enc=\"123456\".MDString(\"abc\");// MD5加盐\nvar enc=\"123456\".MDString2();// MD5两次\nvar enc=\"123456\".MDString2(\"abc\");// MD5两次加盐\nvar enc=\"123456\".MDString3();// MD5三次\nvar enc=\"123456\".MDString3(\"abc\");// MD5三次加盐\n\nstring aes = \"123456\".AESEncrypt();// AES加密为密文\nstring s = aes.AESDecrypt(); //AES解密为明文\nstring aes = \"123456\".AESEncrypt(\"abc\");// AES密钥加密为密文\nstring s = aes.AESDecrypt(\"abc\"); //AES密钥解密为明文\n\nstring enc = \"123456\".DesEncrypt();// DES加密为密文\nstring s = enc.DesDecrypt(); //DES解密为明文\nstring enc = \"123456\".DesEncrypt(\"abcdefgh\");// DES密钥加密为密文\nstring s = enc.DesDecrypt(\"abcdefgh\"); //DES密钥解密为明文\n\nRsaKey rsaKey = RsaCrypt.GenerateRsaKeys();// 生成RSA密钥对\nstring encrypt = \"123456\".RSAEncrypt(rsaKey.PublicKey);// 公钥加密\nstring s = encrypt.RSADecrypt(rsaKey.PrivateKey);// 私钥解密\n\nstring s = \"123\".Crc32();// 生成crc32摘要\nstring s = \"123\".Crc64();// 生成crc64摘要\nstring s = \"123\".SHA256();// 生成SHA256摘要\n\n// 零宽字符串，通常用作文章暗水印，以一种看不见的字符插入到文本中，使攻击者无法直接识别文本内容，从而起到保护文章的作用，可通过代码把水印还原出来取证。\nstring pub=\"hello,world!\";\nstring hidden=\"ldqk\";\nvar str = pub.InjectZeroWidthString(hidden); // 扩展函数调用：将\"ldqk\"以零宽字符串的方式隐藏在\"hello,world!\"中\nvar str = ZeroWidthCodec.Encrypt(pub,hidden); // 类调用：将\"ldqk\"以零宽字符串的方式隐藏在\"hello,world!\"中\nvar dec = str.DecodeZeroWidthString(); // 扩展函数调用：将包含零宽字符串的密文解密出隐藏字符串\"ldqk\"\nvar dec = ZeroWidthCodec.Decrypt(str); // 类调用：将包含零宽字符串的密文解密出隐藏字符串\"ldqk\"\nvar enc = hidden.EncodeToZeroWidthText(); // 扩展函数调用：将字符串编码成零宽字符串\nvar enc = ZeroWidthCodec.Encode(str); // 类调用：将字符串编码成零宽字符串\n```\n\n### 16.实体校验\n\n```csharp\npublic class MyClass\n{\n    [IsEmail] //可在appsetting.json中添加EmailDomainWhiteList配置邮箱域名白名单，逗号分隔\n    public string Email { get; set; }\n\n    [IsPhone]\n    public string PhoneNumber { get; set; }\n\n    [IsLandline]\n    public string Landline { get; set; }\n\n    [IsIPAddress]\n    public string IP { get; set; }\n\n    [MinValue(0, ErrorMessage = \"年龄最小为0岁\"), MaxValue(100, ErrorMessage = \"年龄最大100岁\")]\n    public int Age { get; set; }\n\n    [ComplexPassword]//密码复杂度校验，默认最小长度6，最大长度30，必须包含数字、字母、特殊符号\n    public string Password { get; set; }\n  \n    [ComplexPassword(MustNumber=true,MustLetter=true,MustSymbol=true)]//密码复杂度校验，默认最小长度6，最大长度30，手动配置必须包含数字、字母、特殊符号\n    public string Password { get; set; }\n  \n    [ComplexPassword(4,12)]//密码复杂度校验，配置最小长度4，最大长度12\n    public string Password { get; set; }\n  \n    [EnumOf] // 检测是否是有效枚举值\n    public MyEnum MyEnum { get; set; }\n  \n    [MinItemsCount(1)] // 检测集合元素最少1个\n    public List\u003cstring\u003e Strs { get; set; }\n  \n    [UnifiedSocialCreditCode] // 校验企业统一社会信用代码\n    public string USCC { get; set; }\n}\n```\n\n### 17.HTML操作\n\n```csharp\nList\u003cstring\u003e srcs = \"html\".MatchImgSrcs().ToList();// 获取html字符串里所有的img标签的src属性\nvar imgTags = \"html\".MatchImgTags();//获取html字符串里的所有的img标签\nvar str=\"html\".RemoveHtmlTag(); // 去除html标签\n...\n```\n\n### 18.IP地址和URL\n\n```csharp\nbool inRange = \"192.168.2.2\".IpAddressInRange(\"192.168.1.1\",\"192.168.3.255\");// 判断IP地址是否在这个地址段里\nbool isPrivateIp = \"172.16.23.25\".IsPrivateIP();// 判断是否是私有地址\nbool isExternalAddress = \"http://baidu.com\".IsExternalAddress();// 判断是否是外网的URL\n\n//以下需要配置baiduAK\nstring isp = \"114.114.114.114\".GetISP(); // 获取ISP运营商信息\nPhysicsAddress physicsAddress = \"114.114.114.114\".GetPhysicsAddressInfo().Result;// 获取详细地理信息对象\nTuple\u003cstring, List\u003cstring\u003e\u003e ipAddressInfo = \"114.114.114.114\".GetIPAddressInfo().Result;// 获取详细地理信息集合\n\nuint number=ipAddress.ToUInt32(); // IP地址转10进制\nuint number=\"114.114.114.114\".IPToID(); // IP地址转10进制\n```\n\n### 19.对象属性值合并\n\n```csharp\npublic class MyClass\n{\n    public string A { get; set; }\n\n    public bool? B { get; set; }\n\n    public int? C { get; set; }\n}\n\nvar a = new MyClass()\n{\n    A = \"aa\"\n};\nvar b = new MyClass()\n{\n    B = true\n};\nvar c = new MyClass()\n{\n    C = 3\n};\nvar merge = a.Merge(b, c); // 合并后对象：A = \"aa\"，B = true，C = 3\n```\n\n### 20.元素去重\n\n```csharp\nvar list = new List\u003cMyClass\u003e()\n{\n    new MyClass()\n    {\n        Email = \"1@1.cn\"\n    },\n    new MyClass()\n    {\n        Email = \"1@1.cn\"\n    },\n    new MyClass()\n    {\n        Email = \"1@1.cn\"\n    }\n};\nList\u003cMyClass\u003e classes = list.DistinctBy(c =\u003e c.Email).ToList();\nConsole.WriteLine(classes.Count==1);//True\n```\n\n### 21.枚举扩展\n\n```csharp\n[Flags]\npublic enum MyEnum\n{\n    [Display(Name = \"读\")]\n    [Description(\"读\")]\n    [EnumDescription(\"读取操作\",\"读\",\"zh-CN\")] // 多语言枚举描述\n    [EnumDescription(\"Read\",\"Read\",\"en-US\")]\n    Read=1,\n  \n    [Display(Name = \"写\")]\n    [Description(\"写\")]\n    Write=2,\n\n    Delete=4,\n\n    All=8\n}\n```\n\n```csharp\nDictionary\u003cint, string\u003e dic1 = typeof(MyEnum).GetDictionary();// 获取枚举值和字符串表示的字典映射\nvar dic2 = typeof(MyEnum).GetDescriptionAndValue();// 获取字符串表示和枚举值的字典映射\nstring desc = MyEnum.Read.GetDescription();// 获取Description标签\nstring display = MyEnum.Read.GetDisplay();// 获取Display标签的Name属性\nvar value = typeof(MyEnum).GetValue(\"Read\");//获取字符串表示值对应的枚举值\n```\n\n```csharp\nvar op=MyEnum.Read|MyEnum.Write|MyEnum.Delete;\nvar enums=op.Split(); // 拆分枚举值，得到枚举数组，这个函数建议使用在按位定值的枚举\n```\n\n### 22.定长队列和ConcurrentHashSet实现\n\n`如果是.NET5及以上，推荐使用框架自带的Channel实现该功能`\n\n```csharp\nLimitedQueue\u003cstring\u003e queue = new LimitedQueue\u003cstring\u003e(32);// 声明一个容量为32个元素的定长队列\nConcurrentLimitedQueue\u003cstring\u003e queue = new ConcurrentLimitedQueue\u003cstring\u003e(32);// 声明一个容量为32个元素的线程安全的定长队列\n```\n\n```csharp\nvar set = new ConcurrentHashSet\u003cstring\u003e(); // 用法和hashset保持一致\n```\n\n### 23.反射操作\n\n```csharp\nMyClass myClass = new MyClass();\nPropertyInfo[] properties = myClass.GetProperties();// 获取属性列表\nmyClass.SetProperty(\"Email\",\"1@1.cn\");//给对象设置值\nmyClass.DeepClone(); // 对象深拷贝，带嵌套层级的\nmyClass.ToDictionary(); // 对象转字典\nmyClass.ToDynamic(); // 对象转换成动态可扩展类型\n```\n\n### 24.邮件发送\n\n```csharp\nnew Email()\n{\n    SmtpServer = \"smtp.masuit.com\",// SMTP服务器\n    SmtpPort = 25, // SMTP服务器端口\n    EnableSsl = true,//使用SSL\n    Username = \"admin@masuit.com\",// 邮箱用户名\n    Password = \"123456\",// 邮箱密码\n    Tos = \"10000@qq.com,10001@qq.com\", //收件人\n    Subject = \"测试邮件\",//邮件标题\n    Body = \"你好啊\",//邮件内容\n}.SendAsync(s =\u003e\n{\n    Console.WriteLine(s);// 发送成功后的回调\n});// 异步发送邮件\n```\n\n### 25.图像的简单处理\n\n```csharp\n\"base64\".SaveDataUriAsImageFile();// 将Base64编码转换成图片\n\nusing Image image = Image.Load(@\"D:\\1.jpg\");\nimage.MakeThumbnail(@\"D:\\2.jpg\", 120, 80,ResizeMode.BoxPad);//生成缩略图\n\nvar newBmp = image.BWPic(image.Width, image.Height);//转换成黑白\nvar newBmp = image.CutImage(new Rectangle(0, 0, 1600, 900));//裁剪\nvar newBmp = image.CutAndResize(new Rectangle(0, 0, 1600, 900), 160, 90);//裁剪并缩放\nvar newBmp = image.ResizeImage(160, 90);//改变大小\nvar newBmp = image.RevPicLR();//左右镜像\nvar newBmp = image.RevPicUD();//上下镜像\nvar newBmp =image.LDPic(10); //调整光暗\nvar newBmp =image.RePic(); //反色处理\nvar newBmp =image.Relief(); //浮雕处理\n\nvar gif = Image.Load(@\"D:\\1.gif\");\ngif.GetFrames(@\"D:\\frames\\\"); // 解压gif每帧图片\n\nvar marker=ImageWatermarker(stream);\nstream=maker.AddWatermark(\"水印文字\",\"字体文件\",字体大小,color,水印位置,边距); // 给图片添加水印\nstream=maker.AddWatermark(水印图片,水印位置,边距,字体大小,字体); // 给图片添加水印\n```\n\n```csharp\nvar borderInfo=new ImageBorderRemover(ToleranceMode.Channel).DetectBorders(原始图片); // 检测图片是否包含纯色边框\nnew ImageBorderRemover(ToleranceMode.Channel).RemoveBorders(原始图片,保存图片); // 移除图片的纯色边框并另存为\n```\n\n```csharp\nvar delta = ColorDeltaE.CIE1976(color1,color2); // 计算两个颜色的CIE1976色差\nvar delta = ColorDeltaE.CIE1994(color1,color2); // 计算两个颜色的CIE1994色差\nvar delta = ColorDeltaE.CIE2000(color1,color2); // 计算两个颜色的CIE2000色差\nvar delta = ColorDeltaE.CMC(color1,color2); // 计算两个颜色的CMC(l:c)色差\n```\n```csharp\n// 图像相似度对比\nvar hasher = new ImageHasher();\nvar hash1 = hasher.DifferenceHash256(\"图片1\"); // 使用差分哈希算法计算图像的256位哈希\nvar hash2 = hasher.DifferenceHash256(\"图片2\"); // 使用差分哈希算法计算图像的256位哈希\n//var hash1 = hasher.AverageHash64(\"图片1\"); // 使用平均值算法计算图像的64位哈希\n//var hash2 = hasher.AverageHash64(\"图片2\"); // 使用平均值算法计算图像的64位哈希\n//var hash1 = hasher.DctHash(\"图片1\"); // 使用DCT算法计算图像的64位哈希\n//var hash2 = hasher.DctHash(\"图片2\"); // 使用DCT算法计算图像的64位哈希\n//var hash1 = hasher.MedianHash64(\"图片1\"); // 使用中值算法计算给定图像的64位哈希\n//var hash2 = hasher.MedianHash64(\"图片2\"); // 使用中值算法计算给定图像的64位哈希\nvar sim=ImageHasher.Compare(hash1,hash2); // 图片的相似度，范围：[0,1]\n\nvar imageFormat=stream.GetImageType(); // 获取图片的真实格式\n```\n最佳实践案例，以图搜图：https://github.com/ldqk/ImageSearch\n### 26.随机数\n\n```csharp\nRandom rnd = new Random();\nint num = rnd.StrictNext();//产生真随机数\ndouble gauss = rnd.NextGauss(20,5);//产生正态高斯分布的随机数\nvar s = new NumberFormater(62).ToString(new Random().Next(100000, int.MaxValue));//生成随机字符串\n```\n\n### 27.权重随机筛选功能\n\n```csharp\nvar data=new List\u003cWeightedItem\u003cstring\u003e\u003e()\n{\n     new WeightedItem\u003cstring\u003e(\"A\", 1),\n     new WeightedItem\u003cstring\u003e(\"B\", 3),\n     new WeightedItem\u003cstring\u003e(\"C\", 4),\n     new WeightedItem\u003cstring\u003e(\"D\", 4),\n};\nvar item=data.WeightedItem();//按权重选出1个元素\nvar list=data.WeightedItems(2);//按权重选出2个元素\n```\n\n```csharp\nvar selector = new WeightedSelector\u003cstring\u003e(new List\u003cWeightedItem\u003cstring\u003e\u003e()\n{\n    new WeightedItem\u003cstring\u003e(\"A\", 1),\n    new WeightedItem\u003cstring\u003e(\"B\", 3),\n    new WeightedItem\u003cstring\u003e(\"C\", 4),\n    new WeightedItem\u003cstring\u003e(\"D\", 4),\n});\nvar item = selector.Select();//按权重选出1个元素\nvar list = selector.SelectMultiple(3);//按权重选出3个元素\n```\n\n```csharp\nlist.WeightedItems(3,e=\u003ee.Price); // 按价格权重选出3个元素\nlist.WeightedBy(e=\u003ee.Price); // 按价格权重选出1个元素\n```\n\n### 28.EF Core支持AddOrUpdate方法\n\n```csharp\n/// \u003csummary\u003e\n/// 按Id添加或更新文章实体\n/// \u003c/summary\u003e\npublic override Post SavePost(Post t)\n{\n    DataContext.Set\u003cPost\u003e().AddOrUpdate(t =\u003e t.Id, t);\n    return t;\n}\n```\n\n### 29.敏感信息掩码\n\n```csharp\n\"13123456789\".Mask(); // 131****5678\n\"admin@masuit.com\".MaskEmail(); // a****n@masuit.com\n```\n\n```csharp\n// Attribute的方式为json序列化时进行数据脱敏\npublic class MyClass\n{\n    [JsonConverter(typeof(MaskEmailConverter))] // 请注意命名空间，使用Newtonsoft.Json请导入Masuit.Tools.Systems命名空间，使用System.Text.Json请导入Masuit.Tools.Systems.Text.Json命名空间\n    public string Email { get; set; }\n\n    [JsonConverter(typeof(MaskConverter))] // 请注意命名空间，使用Newtonsoft.Json请导入Masuit.Tools.Systems命名空间，使用System.Text.Json请导入Masuit.Tools.Systems.Text.Json命名空间\n    public string PhoneNumber { get; set; }\n}\n```\n\n### 30.集合扩展\n\n```csharp\nvar list = new List\u003cstring\u003e()\n{\n    \"1\",\"3\",\"3\",\"3\"\n};\nlist.AddRangeIf(s =\u003e s.Length \u003e 1, \"1\", \"11\"); // 将被添加元素中的长度大于1的元素添加到list\nlist.AddRangeIfNotContains(\"1\", \"11\"); // 将被添加元素中不包含的元素添加到list\nlist.RemoveWhere(s =\u003e s.Length\u003c1); // 将集合中长度小于1的元素移除\nlist.InsertAfter(0, \"2\"); // 在第一个元素之后插入\nlist.InsertAfter(s =\u003e s == \"1\", \"2\"); // 在元素\"1\"后插入\n\nvar dic = list.ToDictionarySafety(s =\u003e s); // 安全的转换成字典类型，当键重复时只添加一个键\nvar dic = list.ToConcurrentDictionary(s =\u003e s); // 转换成并发字典类型，当键重复时只添加一个键\nvar dic = list.ToDictionarySafety(s =\u003e s, s =\u003e s.GetHashCode()); // 安全的转换成字典类型，当键重复时只添加一个键\nvar v = dic[x=\u003ex.Key.Contains(\"1\")]; // 字典根据条件取值\ndic[x=\u003ex.Key.Contains(\"1\")]=2; // 字典根据条件赋值\nvar v = dic[(key,value)=\u003ekey.Contains(\"1\")]; // 字典根据条件取值\ndic[(key,value)=\u003ekey.Contains(\"1\")]=2; // 字典根据条件赋值\nvar v = dic[key=\u003ekey.Contains(\"1\")]; // 字典根据key条件取值\ndic[key=\u003ekey.Contains(\"1\")]=2; // 字典根据key条件赋值\nvar v = dic[value=\u003evalue\u003e0]; // 字典根据value条件取值\ndic[value=\u003evalue\u003e0]=2; // 字典根据value条件赋值\ndic.AddOrUpdate(\"4\", 4); // 添加或更新键值对\ndic.AddOrUpdate(new Dictionary\u003cstring, int\u003e()\n{\n    [\"5\"] = 5,[\"55\"]=555\n}); // 批量添加或更新键值对\ndic.AddOrUpdate(\"5\", 6, (s, i) =\u003e 66); // 如果是添加，则值为6，若更新则值为66\ndic.AddOrUpdate(\"5\", 6, 666); // 如果是添加，则值为6，若更新则值为666\ndic.GetOrAdd(\"7\",77); // 字典获取或添加元素\ndic.GetOrAdd(\"7\",()=\u003e77); // 字典获取或添加元素\ndic.AsConcurrentDictionary(); // 普通字典转换成并发字典集合\n\nvar table=list.ToDataTable(); // 转换成DataTable类型\ntable.AddIdentityColumn(); //给DataTable增加一个自增列\ntable.HasRows(); // 检查DataTable 是否有数据行\ntable.ToList\u003cT\u003e(); // datatable转List\nvar set = list.ToHashSet(s=\u003es.Name);// 转HashSet\nvar cts = new CancellationTokenSource(100); //取消口令\nawait list.ForeachAsync(async i=\u003e{\n    await Task.Delay(100);\n    Console.WriteLine(i);\n},cts.Token); // 异步foreach\n\nawait list.ForAsync(async (item,index)=\u003e{\n    await Task.Delay(100);\n    Console.WriteLine(item+\"_\"+index);\n},cts.Token); // 异步for，带索引编号\nawait list.SelectAsync(async i=\u003e{\n    await Task.Delay(100);\n    return i*10;\n}); // 异步Select\nawait list.SelectAsync(async (item,index)=\u003e{\n    await Task.Delay(100);\n    return item*10;\n}); // 异步Select，带索引编号\nstring s=list.Join(\",\");//将字符串集合连接成逗号分隔的单字符串\nvar max=list.MaxOrDefault(); // 取最大值，当集合为空的时候不会报错\nvar max=list.MaxOrDefault(selector); // 取最大值，当集合为空的时候不会报错\nvar max=list.MaxOrDefault(selector,default); // 取最大值，当集合为空的时候不会报错\nvar max=list.MinOrDefault(); // 取最小值，当集合为空的时候不会报错\nvar max=list.MinOrDefault(selector); // 取最小值，当集合为空的时候不会报错\nvar max=list.MinOrDefault(selector,default); // 取最小值，当集合为空的时候不会报错\nvar stdDev=list.Select(s=\u003es.ConvertTo\u003cint\u003e()).StandardDeviation(); // 求标准差\n\nvar pages=queryable.ToPagedList(1,10); // 分页查询\nvar pages=await queryable.ToPagedListAsync(1,10); // 分页查询\n\nvar nums=Enumerable.Range(1, 10).ExceptBy(Enumerable.Range(5, 10), i =\u003e i); // 按字段取差集\nvar nums=Enumerable.Range(1, 10).IntersectBy(Enumerable.Range(5, 10), i =\u003e i); // 按字段取交集\nvar nums=Enumerable.Range(1, 10).SequenceEqual(Enumerable.Range(5, 10), i =\u003e i); // 判断序列相等\nvar nums=Enumerable.Range(1, 10).OrderByRandom(); // 随机排序\n\n// 多个集合取交集\nvar list=new List\u003cList\u003cMyClass\u003e\u003e(){\n    new List\u003cMyClass\u003e(){\n        new MyClass(){Name=\"aa\",Age=11},\n        new MyClass(){Name=\"bb\",Age=12},\n        new MyClass(){Name=\"cc\",Age=13},\n    },\n    new List\u003cMyClass\u003e(){\n        new MyClass(){Name=\"bb\",Age=12},\n        new MyClass(){Name=\"cc\",Age=13},\n        new MyClass(){Name=\"dd\",Age=14},\n    },\n    new List\u003cMyClass\u003e(){\n        new MyClass(){Name=\"cc\",Age=13},\n        new MyClass(){Name=\"dd\",Age=14},\n        new MyClass(){Name=\"ee\",Age=15},\n    },\n};\nvar sect=list.IntersectAll(m=\u003em.Name); // new MyClass(){Name=\"cc\",Age=13}\n\nvar list=new List\u003cList\u003cint\u003e\u003e(){\n    new(){1,2,3},\n    new(){2,3,4},\n    new(){3,4,5}\n};\nvar sect=list.IntersectAll();// [3]\n\n// 集合元素改变其索引位置\nlist.ChangeIndex(item,3); // 将元素item的索引位置变为第3个\nlist.ChangeIndex(t=\u003et.Id==\"123\",2); // 将id为123的元素的索引位置变为第2个\n\nvar item=list.Percentile(50); // 取第50%分位数的元素\n\n// 比较两个集合的差异\nvar (adds,removes,updates)=list1.CompareChanges(list2,x=\u003ex.Id); // 按Id字段比较两个集合的差异，返回新增、删除、更新的元素\nvar (adds,removes,updates)=list1.CompareChanges(list2,x=\u003ex.Id+x.Name); // 按Id和Name字段比较两个集合的差异，返回新增、删除、更新的元素\nvar (adds,removes,updates)=list1.CompareChangesPlus(list2,x=\u003ex.Id+x.Name); // 按Id和Name字段比较两个集合的差异，返回新增、删除、更新的元素，其中updates返回的每个元素包含了旧值和新值\n```\n\n### 31.Mime类型\n\n```csharp\nvar mimeMapper = new MimeMapper();\nvar ext = mimeMapper.GetExtensionFromMime(\"image/jpeg\"); // .jpg\nvar mime = mimeMapper.GetMimeFromExtension(\".jpg\"); // image/jpeg\n\nContentType常量库：\nvar type=ContentType.Exe; // application/octet-stream\nvar type=ContentType.Jpeg; // image/jpeg\nvar type=DefaultMimeItems.Items.FirstOrDefault(t=\u003et.Extension==\"jpg\"); // image/jpeg\n```\n\n### 32.日期时间扩展\n\n```csharp\nvar weeks=DateTime.Now.GetWeekAmount(); // 获取当前所在年一共有多少周\nvar week = DateTime.Now.WeekOfYear(); // 获取当前所在年的第几周\nvar week = DateTime.Now.WeekOfYear(DayOfWeek.Monday); // 获取当前所在年的第几周,并指定星期几是每周第一天\n\ndouble milliseconds = DateTime.Now.GetTotalMilliseconds();// 获取毫秒级时间戳\ndouble microseconds = DateTime.Now.GetTotalMicroseconds();// 获取微秒级时间戳\ndouble nanoseconds = DateTime.Now.GetTotalNanoseconds();// 获取纳秒级时间戳\ndouble seconds = DateTime.Now.GetTotalSeconds();// 获取秒级时间戳\ndouble minutes = DateTime.Now.GetTotalMinutes();// 获取分钟级时间戳\n\nvar indate=DateTime.Parse(\"2020-8-3\").In(DateTime.Parse(\"2020-8-2\"),DateTime.Parse(\"2020-8-4\"));//true\nDateTime time=\"2021-1-1 8:00:00\".ToDateTime(); //字符串转DateTime\n\n//时间段计算工具\nvar range = new DateTimeRange(DateTime.Parse(\"2020-8-3\"), DateTime.Parse(\"2020-8-5\"));\nrange.Union(DateTime.Parse(\"2020-8-4\"), DateTime.Parse(\"2020-8-6\")); //连接两个时间段，结果：2020-8-3~2020-8-6\nrange.In(DateTime.Parse(\"2020-8-3\"), DateTime.Parse(\"2020-8-6\"));//判断是否在某个时间段内，true\nvar (intersected,range2) = range.Intersect(DateTime.Parse(\"2020-8-4\"), DateTime.Parse(\"2020-8-6\"));//两个时间段是否相交，(true,2020-8-3~2020-8-4)\nrange.Contains(DateTime.Parse(\"2020-8-3\"), DateTime.Parse(\"2020-8-4\"));//判断是否包含某个时间段，true\n\nrange.GetUnionSet(List\u003cDateTimeRange\u003e); // 根据某个时间段查找在某批时间段中的最大并集\nrange.GetMaxTimePeriod(List\u003cDateTimeRange\u003e); // 获取一批时间段内存在相互重叠的最大时间段\n\nvar range = DateTime.Now.GetCurrentWeek(); // 获取当前时间所在周的时间区间：2024-08-05 00:00:00~2024-08-11 23:59:59\nvar range = DateTime.Now.GetCurrentMonth(); // 获取当前时间所在月的时间区间：2024-08-01 00:00:00~2024-08-31 23:59:59\nvar range = DateTime.Now.GetCurrentYear(); // 获取当前时间所在年的时间区间：2024-01-01 00:00:00~2024-12-31 23:59:59\nvar range = DateTime.Now.GetCurrentQuarter(); // 获取当前时间所在季度的时间区间：2024-07-01 00:00:00~2024-09-30 23:59:59\nvar range = DateTime.Now.GetCurrentLunarMonth(); // 获取当前时间所在农历月的时间区间：2024-08-04 00:00:00~2024-09-02 23:59:59\nvar range = DateTime.Now.GetCurrentLunarQuarter(); // 获取当前时间所在农历季度的时间区间：2024-08-04 00:00:00~2024-10-31 23:59:59\nvar range = DateTime.Now.GetCurrentLunarYaer(); // 获取当前时间所在农历年的时间区间：2024-02-10 00:00:00~2025-01-28 23:59:59\nvar range = DateTime.Now.GetCurrentSolar(); // 获取当前时间所在季节的时间区间：2024-08-07 00:00:00~2024-11-06 23:59:59\nvar range = DateTime.Now.GetCurrentRange(DateRangeType.Week); // 获取当前时间所在周的时间区间：2024-08-05 00:00:00~2024-08-11 23:59:59\nvar range = DateTime.Now.GetCurrentRange(DateRangeType.Month); // 获取当前时间所在月的时间区间：2024-08-01 00:00:00~2024-08-31 23:59:59\nvar range = DateTime.Now.GetCurrentRange(DateRangeType.Quarter); // 获取当前时间所在季度的时间区间：2024-07-01 00:00:00~2024-09-30 23:59:59\nvar range = DateTime.Now.GetCurrentRange(DateRangeType.Year); // 获取当前时间所在年的时间区间：2024-01-01 00:00:00~2024-12-31 23:59:59\nvar range = DateTime.Now.GetCurrentRange(DateRangeType.LunarMonth); // 获取当前时间所在农历月的时间区间：2024-08-04 00:00:00~2024-09-02 23:59:59\nvar range = DateTime.Now.GetCurrentRange(DateRangeType.LunarQuarter); // 获取当前时间所在农历季度的时间区间：2024-08-04 00:00:00~2024-10-31 23:59:59\nvar range = DateTime.Now.GetCurrentRange(DateRangeType.LunarYear); // 获取当前时间所在农历年的时间区间：2024-02-10 00:00:00~2025-01-28 23:59:59\nvar range = DateTime.Now.GetCurrentRange(DateRangeType.Solar); // 获取当前时间所在季节的时间区间：2024-08-07 00:00:00~2024-11-06 23:59:59\n\n...\n```\n\n### 33.流相关\n\n```csharp\nstream.SaveAsMemoryStream(); // 任意流转换成内存流\nstream.ToArray(); // 任意流转换成二进制数组\nstream.ToArrayAsync(); // 任意流转换成二进制数组\nstream.ShuffleCode(); // 流洗码，在流的末端随即增加几个空字节，重要数据请谨慎使用，可能造成流损坏\n\n// 池化内存流，用法与MemorySteam保持一致\nusing var ms=PooledMemoryStream();\n\n// 大型内存流,最大可支持1TB内存数据，推荐当数据流大于2GB时使用，用法与MemorySteam保持一致\nusing var ms=LargeMemoryStream();\n\n//文件流快速复制\nFileStream fs = new FileStream(@\"D:\\boot.vmdk\", FileMode.OpenOrCreate, FileAccess.ReadWrite);\n{\n        //fs.CopyToFile(@\"D:\\1.bak\");//同步复制大文件\n        fs.CopyToFileAsync(@\"D:\\1.bak\");//异步复制大文件\n        string md5 = fs.GetFileMD5Async().Result;//异步获取文件的MD5\n        string sha1 = fs.GetFileSha1();//异步获取文件的SHA1\n}\nmemoryStream.SaveFile(\"filename\"); // 将内存流转储成文件\n```\n\n### 34.类型操作/字符串\n\n```csharp\n1.2345678901.Digits8(); // 将小数截断为8位\n1.23.ConvertTo\u003cint\u003e(); // 小数转int\n1.23.ConvertTo\u003cT\u003e(); // 小数转T基本类型\nbool b=1.23.TryConvertTo\u003cT\u003e(out result); // 小数转T基本类型\nvar num=1.2345.ToDecimal(2); //转decimal并保留两位小数\n\n1.23.ChangeTypeTo\u003cT\u003e(); //小数转T基本类型,ConvertTo和ChangeTypeTo的区别在于：ConvertTo只适用于基元类型的互转，ChangeTypeTo不仅适用于基元类型的互转还支持数组、字符串的转换(Parse)，ConvertTo的性能更高\n\ntype.IsPrimitive(); // 判断类型是否是值类型\ntype.IsSimpleType(); // 判断类型是否是常见的简单类型，基元类型为 Boolean、 Byte、 SByte、 Int16、 UInt16、 Int32、 UInt32、 Int64、 UInt64、 IntPtr、 UIntPtr、 Char、 Double 、 Single、枚举、Nullable\u003cT\u003e。\ntype.IsSimpleArrayType(); // 判断类型是否是常见类型的 数组形式 类型\ntype.IsSimpleListType(); // 判断类型是否是常见类型的 泛型形式 类型\n\nmyClass.ToJsonString(); //序列化成json字符串\n\nstring s=null;\nbool b=s.IsNullOrEmpty();//判断字符串是否为空\nbool b=s.NotNullOrEmpty();//判断字符串不为空\nstring str=s.IfNullOrEmpty(\"aa\");//如果为空则返回aa\nstring str=s.IfNullOrEmpty(()=\u003e\"aa\");//如果为空则返回aa,延迟执行\n\nbool contains=s.Contains(new[]{\"aa\",\"bb\"});// 检测字符串中是否包含列表中的关键词(快速匹配)\nbool contains=s.ContainsSafety(new[]{\"aa\",\"bb\"});// 检测字符串中是否包含列表中的关键词(安全匹配)，没有计时攻击风险\nbool contains=s.EndsWith(new[]{\"aa\",\"bb\"});// 检测字符串中是否以列表中的任意关键词结尾\nbool contains=s.StartsWith(new[]{\"aa\",\"bb\"});// 检测字符串中是否以列表中的任意关键词开头\n\nstring str=s.Take(10); // 取字符串前10个字符\n\nbool emoji=s.MatchEmoji(); // 匹配字符串是否包含emoji\n\nvar width=str.StringWidth(14); // 计算字符串以14号字体大小的渲染宽度像素\nvar width=str.StringWidth(\"微软雅黑\",14); // 计算字符串以14号字体大小的微软雅黑字体的渲染宽度像素\nvar width=str.CharacterCount(); // 获取字符串的字符数\nvar width=str.BytesCount(); // 获取字符串的字节数\n\n\nvar s = \"🤔1🥳a👨‍👩‍👧‍👦啊\";\nConsole.WriteLine(\"长度：\" + s.Length); // 18\nConsole.WriteLine(\"宽度：\" + s.StringWidth()); // 7\nConsole.WriteLine(\"字节数：\" + s.BytesCount()); // 38\nConsole.WriteLine(\"字符数：\" + s.CharacterCount()); // 6\n\nvar s=\"aa\".ToSBC(); // 转换为全角\nvar s=\"ａａ\".ToDBC(); // 转换为半角\n```\n\n### 35.INI配置文件操作\n\n```csharp\nIniFile ini=new IniFile(\"X:\\\\filename.ini\"); // 需要绝对路径，否则会写到C:\\Windows目录下去\nini.SetValue(section,key,value); // 写值\nvar value=ini.GetValue(section,key); // 读值\nvar value=ini.GetValue\u003cEnum\u003e(section,key); // 读值并转换类型\nvar sections=ini.GetSections(); // 获取所有配置节\nvar section=ini.GetSection(section); // 获取配置节\nvar myclass=ini.GetSection\u003cMyClass\u003e(section); // 获取指定配置节并绑定到对象\nini.ClearAllSection(); // 清空所有配置节\nini.ClearSection(section); // 清空配置节\nini.Save(); // 保存ini文件\nini.Reload(); // 重新加载ini文件\n\n\nclass MyClass\n{\n    [IniProperty(\"str_value\")] // 设置别名\n    public string StringValue { get; set; }\n}\n```\n\n### 36.雷达图计算引擎\n\n应用场景：计算两个多边形的相似度，用户画像之类的\n\n```csharp\nvar points=RadarChartEngine.ComputeIntersection(chart1,chart2); //获取两个多边形的相交区域\npoints.ComputeArea(); //计算多边形面积\n```\n\n### 37.树形结构实现\n\n基本接口类：\nITreeChildren：带Children属性的接口  \nITreeParent：带Parent属性的接口  \nITree：继承ITreeParent和ITreeChildren，同时多了Name属性  \nITreeEntity：继承ITreeChildren，同时多了Id和ParentId属性  \n\n相关扩展方法：\n\n```csharp\ntrees.Filter(func); // 从树形集合中过滤\ntrees.Flatten(); // 将数据平铺开\ntree.AllChildren(); // 获取所有的子级\ntree.AllParent(); // 获取所有的父级\ntree.IsRoot(); // 是否是根节点\ntree.IsLeaf(); // 是否是叶子节点\ntree.Level(); // 所处深度/层级\ntree.Path(); // 全路径\n\nvar tree=list.ToTree();//集合元素继承自ITreeEntity\u003cT,TKey\u003e或ITreeEntity\u003cT\u003e的集合转换成树形结构\nvar tree=list.ToTree(c =\u003e c.Id, c =\u003e c.Pid);//集合元素继承自ITreeParent\u003cT\u003e, ITreeChildren\u003cT\u003e的集合转换成树形结构\nvar tree=list.ToTreeGeneral(c =\u003e c.Id, c =\u003e c.Pid);//一般的集合转换成树形结构\n```\n![Tree结构性能跑分](https://foruda.gitee.com/images/1715417742882952476/d429d7c3_1534928.png \"屏幕截图\")\n\n### 38.简单的Excel导出\n\n需要额外依赖包：`Masuit.Tools.Excel`\n\n```csharp\nvar stream=list.Select(item=\u003enew{\n    姓名=item.Name,\n    年龄=item.Age,\n    item.Gender,\n    Avatar=Image.FromStream(filestream) //图片列\n}).ToDataTable().ToExcel(\"Sheet1\"); //自定义列名导出\nvar stream=list.ToDataTable(\"Sheet1\").ToExcel(\"文件密码\");\n```\n\n#### 一些约定规则：\n\n1. 图片列支持Stream、Bitmap、IEnumerable `\u003cStream\u003e`、IEnumerable `\u003cBitmap\u003e`、IDictionary\u003cstring,Stream\u003e、IDictionary\u003cstring,MemoryStream\u003e、IDictionary\u003cstring,Bitmap\u003e类型；\n2. 其中，如果是IDictionary类型的图片列，字典的键为图片超链接的完整url；\n3. 默认字段名作为列名导出；\n4. 若list是一个具体的强类型，默认会先查找每个字段的Description标记，若有Description标记，则取Description标记作为列名显示\n5. ToExcel方法支持DataTable、List `\u003cDataTable\u003e`、Dictionary\u003cstring, DataTable\u003e类型的直接调用\n\n### 39.EFCore实用扩展\n\n#### 跟踪实体变更对比\n\n获取指定实体的变更\n\n```csharp\nvar changes=dbContext.GetChanges\u003cTEntity\u003e();//获取变更字段信息\nvar added=dbContext.GetAdded\u003cTEntity\u003e();//获取添加的实体字段信息\nvar removed=dbContext.GetRemoved\u003cTEntity\u003e();//获取被移除的实体字段信息  \nvar allchanges=dbContext.GetAllChanges\u003cTEntity\u003e();//获取增删改的实体字段信息  \n```\n\n获取所有实体的变更\n\n```csharp\nvar changes=dbContext.GetChanges();//获取变更字段信息\nvar added=dbContext.GetAdded();//获取添加的实体字段信息\nvar removed=dbContext.GetRemoved();//获取被移除的实体字段信息  \nvar allchanges=dbContext.GetAllChanges();//获取增删改的实体字段信息  \n```\n\n对比信息包含属性信息、旧值、新值、实体信息、键信息、变更状态等\n\n#### nolock查询\n\nSQL Server：\n\n上下文注入Interceptor即可在任何查询时使用nolock查询\n\n```csharp\nservices.AddDbContext\u003cTContext\u003e(opt =\u003e opt.UseSqlserver(\"ConnString\", builder =\u003e builder.AddInterceptors(new WithNoLockInterceptor(true))); // 启用全局nolock查询\nservices.AddDbContext\u003cTContext\u003e(opt =\u003e opt.UseSqlserver(\"ConnString\", builder =\u003e builder.AddInterceptors(new WithNoLockInterceptor())); // 按需启用全局nolock查询\n\n// 按需启用全局nolock查询，执行单个nolock查询\nawait dbContext.Users.Where(x=\u003ex.Name==\"aaa\").WithNolock().ToListAsync();\n```\n\n其他通用数据库：\n\nnolock本质是开启一个 `读未提交`级别的事务，此时的查询性能最好，但有可能会读取到脏数据。\n\n```csharp\ndbcontext.NoLock(ctx=\u003ectx.Users...ToList()); // 开启一个nolock上下文，上下文范围内自动nolock\n\n// 执行单个nolock查询\nawait dbcontext.Users.Where(x=\u003ex.Name==\"aaa\").ToListWithNoLockAsync(); \nawait dbcontext.Users.Where(x=\u003ex.Name==\"aaa\").FirstOrDefaultWithNoLockAsync(); \nawait dbcontext.Users.Where(x=\u003ex.Name==\"aaa\").SingleOrDefaultWithNoLockAsync(); \nawait dbcontext.Users.AnyWithNoLockAsync(x=\u003ex.Name==\"aaa\"); \nawait dbcontext.Users.AllWithNoLockAsync(x=\u003ex.Name==\"aaa\"); \nawait dbcontext.Users.CountWithNoLockAsync(x=\u003ex.Name==\"aaa\"); \n\n// 如果在上下文开启了重试机制,执行单个nolock查询需要开启策略查询\nservices.AddDbContext\u003cTContext\u003e(opt =\u003e opt.UseNpgsql(\"ConnString\", builder =\u003e builder.EnableRetryOnFailure(10));\n\n// 执行策略\ndbcontext.ExecutionStrategy(stg=\u003estg.NoLock(ctx=\u003ectx.Users...ToList()));\ndbcontext.ExecutionStrategy(ctx=\u003ectx.Users.Where(x=\u003ex.Name==\"aaa\").ToListWithNoLockAsync());\n```\n\n#### 自动递归式Include扩展(通常用于树形表)\n\n```csharp\n// 如果你现在正在写类似这样的代码：\ndbcontext.Category.Include(c=\u003ec.Children).ThenInclude(c=\u003ec.Children).ThenInclude(c=\u003ec.Children).ThenInclude(c=\u003ec.Children);\n\n// 那么可以改成这样：\ndbcontext.Category.IncludeRecursive(4, c =\u003e c.Children); // 自动Include 4次\n```\n\n### 40.任何类型支持链式调用\n\n```csharp\na.Next(func1).Next(func2).Next(func3);\n\"123\".Next(s=\u003es.ToInt32()).Next(x=\u003ex*2).Next(x=\u003eMath.Log(x));\n```\n\n### 41.Newtonsoft.Json和System.Text.Json的只允许字段(反)序列化行为的契约解释器\n\n#### DeserializeOnlyContractResolver\n\n该解释器针对类属性被DeserializeOnlyJsonPropertyAttribute/SerializeIgnoreAttribute标记的，在反序列化的时候生效，在序列化的时候忽略;被SerializeOnlyJsonPropertyAttribute/DeserializeIgnoreAttribute标记的，在序列化的时候生效，在反序列化的时候忽略\n\n```csharp\npublic class ClassDto\n    {\n        // 序列化时忽略这个属性/反序列化时加载这个属性\n        [DeserializeOnlyJsonProperty]\n        //[SerializeIgnore]\n        public string MyProperty { get; set; }\n\n        // 反序列化时忽略这个属性/序列化时加载这个属性\n        [SerializeOnlyJsonProperty]\n        //[DeserializeIgnore]\n        public int Num { get; set; }\n    }\n  \n    // Newtonsoft.Json\n    JsonConvert.SerializeObject(new MyClass(),new JsonSerializerSettings()\n    {\n        ContractResolver = new DeserializeOnlyContractResolver() // 配置使用DeserializeOnlyContractResolver解释器\n    });\n\n    // System.Text.Json\n    JsonSerializer.Serialize(object, new JsonSerializerOptions() { TypeInfoResolver = new SerializeIgnoreResolver() });\n```\n\n如果是WebAPI全局使用：\n\n```csharp\n        //在Startup.ConfigureServices中\n        services.AddMvc().AddNewtonsoftJson(options =\u003e\n             {\n                 var resolver = new DeserializeOnlyContractResolver();\n                 resolver.NamingStrategy = new CamelCaseNamingStrategy();\n                 options.SerializerSettings.ContractResolver = resolver;\n             });\n```\n\n#### FallbackJsonPropertyResolver\n\n该解释器针对某个属性设置多个别名，反序列化时支持多个别名key进行绑定，弥补官方JsonProperty别名属性只能设置单一别名的不足\n\n```csharp\n    public class ClassDto\n    {\n        [FallbackJsonProperty(\"MyProperty\",\"a\",\"b\")]\n        public string MyProperty { get; set; }\n\n        public int Num { get; set; }\n    }\n  \n    JsonConvert.SerializeObject(new MyClass(),new JsonSerializerSettings()\n    {\n        ContractResolver = new FallbackJsonPropertyResolver() // 配置使用FallbackJsonPropertyResolver解释器\n    });\n```\n\n#### CompositeContractResolver\n\n该解释器是DeserializeOnlyContractResolver和FallbackJsonPropertyResolver的融合版\n\n### 42. ASP.NET Core Action同时支持queryString、表单和json请求类型的模型绑点器BodyOrDefaultModelBinder\n\n用法：\n引入包：`Masuit.Tools.AspNetCore`\n\n```shell\nPM\u003e Install-Package Masuit.Tools.AspNetCore\n```\n\nStartup配置：\n\n```csharp\napp.UseBodyOrDefaultModelBinder();\n```\n\n在action的参数模型前打上标记：`[FromBodyOrDefault]`即可，示例代码如下：\n\n```csharp\n        [HttpGet(\"query\"),HttpPost(\"query\")]\n        public IActionResult Query([FromBodyOrDefault]QueryModel query)\n        {\n            return Ok(...);\n        }\n  \n        [HttpGet(\"query\"),HttpPost(\"query\")]\n        public IActionResult Query([FromBodyOrDefault]int id,[FromBodyOrDefault]string name)\n        {\n            return Ok(...);\n        }\n```\n\n### 43. 字符串SimHash相似度算法\n\n```csharp\nvar dis=\"12345678\".HammingDistance(\"1234567\");\nvar dis=new SimHash(\"12345678\").HammingDistance(new SimHash(\"1234567\"));\n```\n\n### 44. 真实文件类型探测/文本编码检测\n\n```csharp\nvar encoding=new FileInfo(filepath).GetEncoding(); // 获取文件编码(扩展调用)\nvar encoding=stream.GetEncoding(); // 获取流的编码(扩展调用)\nvar encoding=TextEncodingDetector.GetEncoding(filepath); // 获取文件编码(类调用)\n\n// 多种方式，任君调用\nvar detector=new FileInfo(filepath).DetectFiletype(); // 扩展调用\n//var detector=File.OpenRead(filepath).DetectFiletype(); // 流扩展调用\n//var detector=FileSignatureDetector.DetectFiletype(filepath); // 类调用\n\ndetector.Precondition;//基础文件类型\ndetector.Extension;//真实扩展名\ndetector.MimeType;//MimeType\ndetector.FormatCategories;//格式类别\n```\n\n#### 默认支持的文件类型\n\n|   扩展名   |                              说明                              |\n| :--------: | :-------------------------------------------------------------: |\n|    3GP    |                          3GPP, 3GPP 2                          |\n|     7Z     |                              7-Zip                              |\n|    APK    |                    ZIP based Android Package                    |\n|    AVI    |                     Audio-Video Interleave                     |\n|     SH     |                          Shell Script                          |\n|   BPLIST   |                      Binary Property List                      |\n|  BMP, DIB  |                             Bitmap                             |\n|    BZ2    |                       Bunzip2 Compressed                       |\n|    CAB    |                        Microsoft Cabinet                        |\n|   CLASS   |                          Java Bytecode                          |\n|   CONFIG   |                     .NET Configuration File                     |\n| CRT, CERT |                           Certificate                           |\n|    CUR    |                             Cursor                             |\n|     DB     |              Windows Thumbs.db Thumbnail Database              |\n|    DDS    |                       DirectDraw Surface                       |\n|    DLL    |                 Windows Dynamic Linkage Library                 |\n|    DMG    |                     Apple Disk Mount Image                     |\n|    DMP    |                    Windows Memory Dump File                    |\n|    DOC    |             Microsoft Office Word 97-2003 Document             |\n|    DOCX    |             Microsoft Office Word OpenXML Document             |\n|    EPUB    |                         e-Pub Document                         |\n|    EXE    |                        Windows Executive                        |\n|    FLAC    |                         Loseless Audio                         |\n|    FLV    |                           Flash Video                           |\n|    GIF    |                   Graphics Interchage Format                   |\n|     GZ     |                          GZ Compressed                          |\n|    HDP    |                     HD Photo(JPEG XR) Image                     |\n|    HWP    |                   Legacy HWP, HWPML, CFBF HWP                   |\n|    ICO    |                              Icon                              |\n|    INI    |                       Initialization File                       |\n|    ISO    |                       ISO-9660 Disc Image                       |\n|    LNK    |                      Windows Shortcut Link                      |\n|    JP2    |                         JPEG 2000 Image                         |\n| JPG, JPEG |             Joint Photographic Experts Group Image             |\n|    LZH    |                         LZH Compressed                         |\n|    M4A    |               MP4 Container Contained Audio Only               |\n|    M4V    |                  MP4 Container Contained Video                  |\n|    MID    |                           Midi Sound                           |\n|    MKA    |             Matroska Container Contained Audio Only             |\n|    MKV    |               Matroska Container Contained Video               |\n|    MOV    |                      QuickTime Movie Video                      |\n|    MP4    |                MP4 Container Contained Contents                |\n|    MSI    |                       Microsoft Installer                       |\n|    OGG    |                       OGG Video or Audio                       |\n|    ODF    |                      OpenDocument Formula                      |\n|    ODG    |                      OpenDocument Graphics                      |\n|    ODP    |                    OpenDocument Presentation                    |\n|    ODS    |                    OpenDocument Spreadsheet                    |\n|    ODT    |                        OpenDocument Text                        |\n|    PAK    |                  PAK Archive or Quake Archive                  |\n|    PDB    |                   Microsoft Program Database                   |\n|    PDF    |                    Portable Document Format                    |\n|    PFX    |       Microsoft Personal Information Exchange Certificate       |\n|    PNG    |                 Portable Network Graphics Image                 |\n|    PPT    |          Microsoft Office PowerPoint 97-2003 Document          |\n|    PPTX    |          Microsoft Office PowerPoint OpenXML Document          |\n|    PPSX    | Microsoft Office PowerPoint OpenXML Document for Slideshow only |\n|    PSD    |                       Photoshop Document                       |\n|    RAR    |                        WinRAR Compressed                        |\n|    REG    |                        Windows Registry                        |\n|    RPM    |                 RedHat Package Manager Package                 |\n|    RTF    |                    Rich Text Format Document                    |\n|    SLN    |                Microsoft Visual Studio Solution                |\n|    SRT    |                         SubRip Subtitle                         |\n|    SWF    |                         Shockwave Flash                         |\n| SQLITE, DB |                         SQLite Database                         |\n|    TAR    |             pre-ISO Type and UStar Type TAR Package             |\n|    TIFF    |                 Tagged Image File Format Image                 |\n|    TXT    |                           Plain Text                           |\n|    WAV    |                           Wave Audio                           |\n|    WASM    |                       Binary WebAssembly                       |\n|    WEBM    |                           WebM Video                           |\n|    WEBP    |                           WebP Image                           |\n|    XAR    |                           XAR Package                           |\n|    XLS    |             Microsoft Office Excel 97-2003 Document             |\n|    XLSX    |             Microsoft Office Excep OpenXML Document             |\n|    XML    |               Extensible Markup Language Document               |\n|     Z     |                          Z Compressed                          |\n|    ZIP    |                           ZIP Package                           |\n\n### 45. 动态类型扩展\n\n让动态类型支持属性访问器和索引器调用\n\n```csharp\n        var obj = DynamicFactory.NewObject();\n        obj.Name = \"Masuit\";\n        obj.Age = 18;\n        obj[\"Gender\"]=\"男\"\n        obj.MyClass = DynamicFactory.WithObject(new\n        {\n            X = 10,\n            Y = 20,\n            Z = new List\u003cint\u003e { 1, 2, 3, 4, 5 }\n        });\n        Assert.Equal(obj.Name, obj[\"Name\"]);\n        Assert.Equal(obj[\"Gender\"], obj.Gender);\n        Assert.Equal(obj[\"MyClass\"][\"X\"], obj.MyClass.X);\n        Assert.Equal(obj.MyClass.Z[2], obj[\"MyClass\"][\"Z\"][2]);\n```\n\n普通类型转换成动态类型\n\n```csharp\n        var obj = new\n        {\n            Name = \"Masuit\"\n        }.ToDynamic();\n        obj.Age = 18;\n        obj.MyClass = new\n        {\n            X = 10,\n            Y = 20,\n            Z = new List\u003cint\u003e { 1, 2, 3, 4, 5 }\n        }.ToDynamic();\n        obj.Prop = \"test\";\n        _ = obj - \"Prop\"; // 删除属性\n        _ = obj + \"Prop\"; // 增加属性\n\n        Assert.Equal(obj.Name, obj[\"Name\"]);\n        Assert.Equal(obj[\"MyClass\"][\"X\"], obj.MyClass.X);\n```\n\n### 46. 反病毒(仅支持Windows)\n\n```csharp\n// 要求系统WindowsDefender没有被停掉\nvar result = WindowsDefenderScanService.ScanFile(@\"Y:\\1.exe\"); // 扫描文件\nvar result = WindowsDefenderScanService.ScanDirectory(@\"Y:\\\"); // 扫描文件夹\nvar result = WindowsDefenderScanService.ScanStream(stream); // 扫描文件流\n\n// 要求C:\\Windows\\System32\\amsi.dll文件存在，可在WindowsDefender停止时工作\nAmsiScanService.Scan(stream); // 扫描文件流\nAmsiScanService.Scan(@\"Y:\\1.exe\"); // 扫描文件\nAmsiScanService.Scan(bytes); // 扫描二进制数组\n```\n\n### 47. 生成验证码\n\n```csharp\nvar code=ValidateCode.ValidateCode(6); // 生成6位长度的验证码\nvar stream=code.CreateValidateGraphic(); // 生成验证码图片流\n```\n\n### 48. DistributedCache扩展\n\n```csharp\nvar item=cache.Get\u003cT\u003e(key); // 获取值\nvar item=cache.GetOrAdd\u003cT\u003e(key,value); // 获取或添加值\nvar item=cache.GetOrAdd\u003cT\u003e(key,valueFactory); // 获取或添加值\ncache.Set\u003cT\u003e(key,value); // 设置值\ncache.AddOrUpdate\u003cT\u003e(key,value,valueFactory); // 添加或更新值\n```\n\n### 49. ViewData扩展\n\n```csharp\nvar item=ViewData.GetValue\u003cT\u003e(key);//获取对象\nvar item=ViewData.GetValueOrDefault\u003cT\u003e(key,defaultValue);//获取对象\nvar item=ViewData.GetValueOrDefault\u003cT\u003e(key,defaultValueFactory);//获取对象\n```\n\n### 50. 线程上下文存取临时值\n\n```csharp\nCurrentContext\u003cT\u003e.SetData(value);//设置值\nvar item=CurrentContext\u003cT\u003e.GetData();//获取值\n\nCurrentContext.SetData(value);//设置值\nvar item=CurrentContext.GetData\u003cT\u003e();//获取值\n```\n\n### 51. ASP.NET Core自动扫描注册服务\n包：Masuit.Tools.AspNetCore  \n\n```csharp\n// 自动扫描注册服务\nservices.AutoRegisterServices();\n\n// 需要自动注册的服务打上ServiceInject标记即可。\n[ServiceInject(ServiceLifetime.Scoped)]\npublic class MyClass:MyInterface{...}\n\n[ServiceInject(ServiceLifetime.Scoped)]\npublic class MyService{...}\n```\n\n### 52. 文本对比(支持html和纯文本)\n集成案例：https://masuit.org/1889/history\n```csharp\nvar text1 = \"\u003ch1\u003e你好 UEditorPlus\u003c/h1\u003e\u003cp\u003eUEditorPlus 是基于 UEditor 二次开发的富文本编辑器，让 UEditor \u003cspan style=\\\"color: #E36C09;\\\"\u003e焕\u003cspan style=\\\"color: #0070C0;\\\"\u003e然\u003c/span\u003e\u003cspan style=\\\"color: #31859B;\\\"\u003e\u003cspan style=\\\"color: #00B050;\\\"\u003e一\u003c/span\u003e\u003cspan style=\\\"color: #FF0000;\\\"\u003e新\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003ctable data-sort=\\\"sortDisabled\\\"\u003e\u003ctbody\u003e\u003ctr class=\\\"firstRow\\\"\u003e\u003ctd valign=\\\"top\\\" style=\\\"word-break: break-all;\\\" rowspan=\\\"1\\\" colspan=\\\"3\\\"\u003e我是表格\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd width=\\\"273\\\" valign=\\\"top\\\" style=\\\"word-break: break-all;\\\"\u003e如果\u003c/td\u003e\u003ctd width=\\\"273\\\" valign=\\\"top\\\" style=\\\"word-break: break-all;\\\"\u003e有一天\u003c/td\u003e\u003ctd width=\\\"273\\\" valign=\\\"top\\\" style=\\\"word-break: break-all;\\\"\u003e我离开了\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd valign=\\\"top\\\" colspan=\\\"1\\\" rowspan=\\\"1\\\" style=\\\"word-break: break-all;\\\"\u003e怎么才能\u003c/td\u003e\u003ctd valign=\\\"top\\\" colspan=\\\"1\\\" rowspan=\\\"1\\\" style=\\\"word-break: break-all;\\\"\u003e证明我\u003c/td\u003e\u003ctd valign=\\\"top\\\" colspan=\\\"1\\\" rowspan=\\\"1\\\" style=\\\"word-break: break-all;\\\"\u003e曾经来过\u003c/td\u003e\u003c/tr\u003e\u003c/tbody\u003e\u003c/table\u003e\u003ch2\u003e公式支持\u003c/h2\u003e\u003cp\u003e\u003cimg src=\\\"https://r.latexeasy.com/image.svg?%5Cint%20%5Cfrac%7B1%7D%7Bx%7D%20dx%20%3D%20%5Cln%20%5Cleft%7C%20x%20%5Cright%7C%20%2B%20C\\\" data-formula-image=\\\"%5Cint%20%5Cfrac%7B1%7D%7Bx%7D%20dx%20%3D%20%5Cln%20%5Cleft%7C%20x%20%5Cright%7C%20%2B%20C\\\"/\u003e\u003c/p\u003e\u003cp\u003e\u003cbr/\u003e\u003c/p\u003e\";\nvar text2 = \"\u003cp\u003eUEditorPlus 是基于 UEditor 二次开发的富文本编辑器，让 UEditor \u003cspan style=\\\"color: #E36C09;\\\"\u003e焕\u003cp style=\\\"color: #0070C0;\\\"\u003e然\u003c/p\u003e\u003cspan style=\\\"color: #31859B;\\\"\u003e\u003cspan style=\\\"color: #00B050;\\\"\u003e一\u003c/span\u003e\u003cspan style=\\\"color: #FF0000;\\\"\u003e新\u003c/span\u003e\u003c/span\u003e\u003c/span\u003e\u003c/p\u003e\u003ctable data-sort=\\\"sortDisabled\\\"\u003e\u003ctbody\u003e\u003ctr class=\\\"firstRow\\\"\u003e\u003ctd valign=\\\"top\\\" style=\\\"word-break: break-all;\\\" rowspan=\\\"1\\\" colspan=\\\"3\\\"\u003e我是表格\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd width=\\\"273\\\" valign=\\\"top\\\" style=\\\"word-break: break-all;\\\"\u003e如果\u003c/td\u003e\u003ctd width=\\\"273\\\" valign=\\\"top\\\" style=\\\"word-break: break-all;\\\"\u003e有一天\u003c/td\u003e\u003ctd width=\\\"273\\\" valign=\\\"top\\\" style=\\\"word-break: break-all;\\\"\u003e我离开了\u003c/td\u003e\u003c/tr\u003e\u003ctr\u003e\u003ctd valign=\\\"top\\\" colspan=\\\"1\\\" rowspan=\\\"1\\\" style=\\\"word-break: break-all;\\\"\u003e怎么才能\u003c/td\u003e\u003ctd valign=\\\"top\\\" colspan=\\\"1\\\" rowspan=\\\"1\\\" style=\\\"word-break: break-all;\\\"\u003e证明我\u003c/td\u003e\u003ctd valign=\\\"top\\\" colspan=\\\"1\\\" rowspan=\\\"1\\\" style=\\\"word-break: break-all;\\\"\u003e曾经来过\u003c/td\u003e\u003c/tr\u003e\u003c/tbody\u003e\u003c/table\u003e\u003cpre class=\\\"brush:html;toolbar:false\\\"\u003e\u0026lt;div\u0026gt;\\r\\n\u0026nbsp;\u0026nbsp;\u0026lt;span\u0026gt;这里是HTML标签\u0026lt;/span\u0026gt;\\r\\n\u0026lt;/div\u0026gt;\u003c/pre\u003e\u003ch2\u003e公式支持23333333\u003c/h2\u003e\u003cp\u003e\u003cimg src=\\\"https://r.latexeasy.com/image.svg?%5Cint%20%5Cfrac%7B1%7D%7Bx%7D%20dx%20%3D%20%5Cln%20%5Cleft%7C%20x%20%5Cright%7C%20%2B%20C\\\" data-formula-image=\\\"%5Cint%20%5Cfrac%7B1%7D%7Bx%7D%20dx%20%3D%20%5Cln%20%5Cleft%7C%20x%20%5Cright%7C%20%2B%20C\\\"/\u003e\u003c/p\u003e\u003cp\u003e\u003cbr/\u003e\u003c/p\u003e\";\n\nvar (html1, html2) = text1.HtmlDiff(text2); // 对比两段文本并分别生成差异\nvar diffs = TextDiffer.Compute(text1, text2); // 对比两段文本并分别生成差异详细记录\nvar patches = DiffPatch.FromDiffs(diffs); // 根据差异信息生成补丁\npatches.ToText(); // 根据补丁记录重建文本\n(string newText, bool[] results) = patches.Apply(text1); // 将变更补丁应用到原始文本1，并返回是否应用成功\nvar text1 = diffs.Text1(); // 根据差异信息还原文本1\nvar text2 = diffs.Text2(); // 根据差异信息还原文本2\nvar delta = diffs.ToDelta(); // 根据差异信息生成类似于git差异的差分记录\nvar diffs = text1.FromDelta(delta); // 根据差分信息生成差异记录\n```\n\n### 53. 房贷试算模型\n\n集成案例：https://masuit.org/tools/loan\n\n**支持多次提前还款和多次调整利率，同时支持提前还款时变更贷款方式和缩短年限，如有利率调整或提前还款计划，因银行计算受实时利率或提前还款违约金影响，本试算模型的计算结果和银行结果大约有1‰的误差，结果仅供参考，请以银行结果为准。**\n\n模拟案例：\n贷款100万，\n初始利率6.27%，\n等额本息方式，\n贷30年，\n首次还款时间2021-2-1。\n\n利率调整：\n2022-1-1利率调整为5.92%，LPR调整\n2023-1-1利率调整为5.85%，LPR调整\n2023-9-25利率调整为4.3%，政策因素银行自动调整\n2025-1-1利率调整为4.2%，LPR调整\n2026-1-1利率调整为4.1%，LPR调整\n\n提前还款计划：\n2022-10-23提前还款10万，贷款方式不变，\n2023-10-11提前还款10万并缩短年限(`实际目前银行政策不允许`)，\n2025-10-12提前还款10万并修改为等额本金方式，\n2026-10-14提前还款10万并以等额本金方式+缩短年限(`实际目前银行政策不允许`)。\n\n计算代码如下：\n\n```csharp\nvar (totalInterest, actualInterest, savedInterest, totalRepayment, actualPayment, paymentPlans) = new LoanModel(1000000, 0.0627m, 360, DateTime.Parse(\"2021-2-1\"))\n{\n    RateAdjustments = new Dictionary\u003cDateTime, decimal?\u003e()\n    {\n        [DateTime.Parse(\"2022-1-1\")] = 0.0592m, // 调整前月供6170.19，调整后月供5948.53\n        [DateTime.Parse(\"2023-1-1\")] = 0.058m, // 调整前月供5948.53，调整后月供5273.92\n        [DateTime.Parse(\"2023-9-25\")] = 0.043m, // 调整前月供5273.92，调整后月供4496.91，调整次月还款5118.55\n        [DateTime.Parse(\"2025-1-1\")] = 0.042m, // 调整前月供4496.91，调整后首月4457.15\n        [DateTime.Parse(\"2026-1-1\")] = 0.041m, // 调整前月供4762.47，调整后月供4702，调整次月还款8.87元(还款方式改为了等额本金)\n    },\n\n    Prepayments = new List\u003cPrepaymentOption\u003e()\n    {\n        new(DateTime.Parse(\"2022-10-23\"), 100000m, false, LoanType.EquivalentInterest), // 提前还款前月供5948.53，提前还款后月供5339.85\n        new(DateTime.Parse(\"2023-10-11\"), 100000m, true, LoanType.EquivalentInterest), // 提前还款前月供5273.92，提前还款后月供4493.84，期数减少64期\n        new(DateTime.Parse(\"2025-10-12\"), 100000m, false, LoanType.EquivalentPrincipal), // 提前还款前月供4771.56，提前还款后月供首月4762.47，每月递减60.4元\n        new(DateTime.Parse(\"2026-10-14\"), 100000m, true, LoanType.EquivalentPrincipal), // 提前还款前月供4260.28，提前还款后月供首月4251.44，每月递减8.84元，期数减少38期\n    }\n}.Payment();\n```\n\n计算结果：\n总利息totalInterest：1221266.8\n实际支付利息actualInterest：403845.58\n提前还款节省利息savedInterest：817421.22\n总提前还款totalRepayment：400000.00\n实际还款总额actualPayment：1403845.58\n总还款期数paymentPlans：258期，**List类型，每条记录可以展示当期的利率，利息，本金，剩余本金等信息**\n\n# Asp.Net MVC和Asp.Net Core的支持断点续传和多线程下载的ResumeFileResult\n\n在ASP.NET Core中通过MVC/WebAPI应用程序传输文件数据时使用断点续传以及多线程下载支持。\n\n它提供了 `ETag`标头以及 `Last-Modified`标头。 它还支持以下前置条件标头：`If-Match`，`If-None-Match`，`If-Modified-Since`，`If-Unmodified-Since`，`If-Range`。\n\n## 支持 ASP.NET Core 2.0+\n\n从.NET Core2.0开始，ASP.NET Core内部支持断点续传。 因此只是对FileResult做了一些扩展。 只留下了“Content-Disposition” Inline的一部分。 所有代码都依赖于基础.NET类。\n\n## 如何使用\n\n### .NET Framework\n\n在你的控制器中，你可以像在 `FileResult`一样的方式使用它。\n\n```csharp\nusing Masuit.Tools.Mvc;\nusing Masuit.Tools.Mvc.ResumeFileResult;\n```\n\n```csharp\nprivate readonly MimeMapper mimeMapper=new MimeMapper(); // 推荐使用依赖注入\n\npublic ActionResult ResumeFileResult()\n{\n    var path = Server.MapPath(\"~/Content/test.mp4\");\n    return new ResumeFileResult(path, mimeMapper.GetMimeFromPath(path), Request);\n}\n\npublic ActionResult ResumeFile()\n{\n    return this.ResumeFile(\"~/Content/test.mp4\", mimeMapper.GetMimeFromPath(path), \"test.mp4\");\n}\n\npublic ActionResult ResumePhysicalFile()\n{\n    return this.ResumePhysicalFile(@\"D:/test.mp4\", mimeMapper.GetMimeFromPath(@\"D:/test.mp4\"), \"test.mp4\");\n}\n```\n\n### Asp.Net Core\n\n要使用ResumeFileResults，必须在 `Startup.cs`的 `ConfigureServices`方法调用中配置服务：\n\n```csharp\nusing Masuit.Tools.AspNetCore.ResumeFileResults.Extensions;\n```\n\n```csharp\npublic void ConfigureServices(IServiceCollection services)\n{\n    services.AddResumeFileResult();\n}\n```\n\n然后在你的控制器中，你可以像在 `FileResult`一样的方式使用它。\n\n\u003cdetails\u003e\n    \u003csummary\u003e点击查看代码\u003c/summary\u003e\n\n```csharp\nusing Masuit.Tools.AspNetCore.ResumeFileResults.Extensions;\n```\n\n```csharp\nprivate const string EntityTag = \"\\\"TestFile\\\"\";\n\nprivate readonly IHostingEnvironment _hostingEnvironment;\n\nprivate readonly DateTimeOffset _lastModified = new DateTimeOffset(2016, 1, 1, 0, 0, 0, TimeSpan.Zero);\n\n/// \u003csummary\u003e\n/// \n/// \u003c/summary\u003e\n/// \u003cparam name=\"hostingEnvironment\"\u003e\u003c/param\u003e\npublic TestController(IHostingEnvironment hostingEnvironment)\n{\n    _hostingEnvironment = hostingEnvironment;\n}\n\n[HttpGet(\"content/{fileName}/{etag}\")]\npublic IActionResult FileContent(bool fileName, bool etag)\n{\n    string webRoot = _hostingEnvironment.WebRootPath;\n    var content = System.IO.File.ReadAllBytes(Path.Combine(webRoot, \"TestFile.txt\"));\n    ResumeFileContentResult result = this.ResumeFile(content, \"text/plain\", fileName ? \"TestFile.txt\" : null, etag ? EntityTag : null);\n    result.LastModified = _lastModified;\n    return result;\n}\n\n[HttpGet(\"content/{fileName}\")]\npublic IActionResult FileContent(bool fileName)\n{\n    string webRoot = _hostingEnvironment.WebRootPath;\n    var content = System.IO.File.ReadAllBytes(Path.Combine(webRoot, \"TestFile.txt\"));\n    var result = new ResumeFileContentResult(content, \"text/plain\")\n    {\n        FileInlineName = \"TestFile.txt\",\n        LastModified = _lastModified\n    };\n    return result;\n}\n\n[HttpHead(\"file\")]\npublic IActionResult FileHead()\n{\n    ResumeVirtualFileResult result = this.ResumeFile(\"TestFile.txt\", \"text/plain\", \"TestFile.txt\", EntityTag);\n    result.LastModified = _lastModified;\n    return result;\n}\n\n[HttpPut(\"file\")]\npublic IActionResult FilePut()\n{\n    ResumeVirtualFileResult result = this.ResumeFile(\"TestFile.txt\", \"text/plain\", \"TestFile.txt\", EntityTag);\n    result.LastModified = _lastModified;\n    return result;\n}\n\n[HttpGet(\"stream/{fileName}/{etag}\")]\npublic IActionResult FileStream(bool fileName, bool etag)\n{\n    string webRoot = _hostingEnvironment.WebRootPath;\n    FileStream stream = System.IO.File.OpenRead(Path.Combine(webRoot, \"TestFile.txt\"));\n\n    ResumeFileStreamResult result = this.ResumeFile(stream, \"text/plain\", fileName ? \"TestFile.txt\" : null, etag ? EntityTag : null);\n    result.LastModified = _lastModified;\n    return result;\n}\n\n[HttpGet(\"stream/{fileName}\")]\npublic IActionResult FileStream(bool fileName)\n{\n    string webRoot = _hostingEnvironment.WebRootPath;\n    FileStream stream = System.IO.File.OpenRead(Path.Combine(webRoot, \"TestFile.txt\"));\n\n    var result = new ResumeFileStreamResult(stream, \"text/plain\")\n    {\n        FileInlineName = \"TestFile.txt\",\n        LastModified = _lastModified\n    };\n\n    return result;\n}\n\n[HttpGet(\"physical/{fileName}/{etag}\")]\npublic IActionResult PhysicalFile(bool fileName, bool etag)\n{\n    string webRoot = _hostingEnvironment.WebRootPath;\n\n    ResumePhysicalFileResult result = this.ResumePhysicalFile(Path.Combine(webRoot, \"TestFile.txt\"), \"text/plain\", fileName ? \"TestFile.txt\" : null, etag ? EntityTag : null);\n    result.LastModified = _lastModified;\n    return result;\n}\n\n[HttpGet(\"physical/{fileName}\")]\npublic IActionResult PhysicalFile(bool fileName)\n{\n    string webRoot = _hostingEnvironment.WebRootPath;\n\n    var result = new ResumePhysicalFileResult(Path.Combine(webRoot, \"TestFile.txt\"), \"text/plain\")\n    {\n        FileInlineName = \"TestFile.txt\",\n        LastModified = _lastModified\n    };\n\n    return result;\n}\n\n[HttpGet(\"virtual/{fileName}/{etag}\")]\npublic IActionResult VirtualFile(bool fileName, bool etag)\n{\n    ResumeVirtualFileResult result = this.ResumeFile(\"TestFile.txt\", \"text/plain\", fileName ? \"TestFile.txt\" : null, etag ? EntityTag : null);\n    result.LastModified = _lastModified;\n    return result;\n}\n```\n\n以上示例将为您的数据提供“Content-Disposition：attachment”。 当没有提供fileName时，数据将作为“Content-Disposition：inline”提供。\n另外，它可以提供 `ETag`和 `LastModified`标头。\n\n```csharp\n[HttpGet(\"virtual/{fileName}\")]\npublic IActionResult VirtualFile(bool fileName)\n{\n    var result = new ResumeVirtualFileResult(\"TestFile.txt\", \"text/plain\")\n    {\n        FileInlineName = \"TestFile.txt\",\n        LastModified = _lastModified\n    };\n    return result;\n}\n```\n\n\u003c/details\u003e\n\n### 推荐项目\n\n基于EntityFrameworkCore和Lucene.NET实现的全文检索搜索引擎：[Masuit.LuceneEFCore.SearchEngine](https://github.com/ldqk/Masuit.LuceneEFCore.SearchEngine \"Masuit.LuceneEFCore.SearchEngine\")\n\n开源博客系统：[Masuit.MyBlogs](https://github.com/ldqk/Masuit.MyBlogs \"Masuit.MyBlogs\")\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fldqk0%2FMasuit.Tools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fldqk0%2FMasuit.Tools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fldqk0%2FMasuit.Tools/lists"}