{"id":13429465,"url":"https://github.com/thangchung/clean-code-dotnet","last_synced_at":"2025-10-05T01:31:41.922Z","repository":{"id":38375896,"uuid":"103086697","full_name":"thangchung/clean-code-dotnet","owner":"thangchung","description":":bathtub:  Clean Code concepts and tools adapted for .NET ","archived":false,"fork":false,"pushed_at":"2024-08-10T06:24:43.000Z","size":38226,"stargazers_count":7540,"open_issues_count":46,"forks_count":1146,"subscribers_count":287,"default_branch":"master","last_synced_at":"2025-09-30T11:02:29.276Z","etag":null,"topics":["aspnet","awesome","azure","best-practices","clean-architecture","clean-code","composition","csharp","dotnet","inheritance","principles","solid"],"latest_commit_sha":null,"homepage":"","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/thangchung.png","metadata":{"files":{"readme":"README-zh.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":null,"patreon":"thangchung","open_collective":"cleancodedotnet","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2017-09-11T03:43:01.000Z","updated_at":"2025-09-30T08:24:58.000Z","dependencies_parsed_at":"2025-04-27T12:21:23.813Z","dependency_job_id":"832642f7-6387-457e-9bfe-22a469885484","html_url":"https://github.com/thangchung/clean-code-dotnet","commit_stats":{"total_commits":167,"total_committers":23,"mean_commits":7.260869565217392,"dds":0.652694610778443,"last_synced_commit":"1c5aaeb55a31fa8786cd919932f7d22b673b7d27"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/thangchung/clean-code-dotnet","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thangchung%2Fclean-code-dotnet","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thangchung%2Fclean-code-dotnet/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thangchung%2Fclean-code-dotnet/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thangchung%2Fclean-code-dotnet/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/thangchung","download_url":"https://codeload.github.com/thangchung/clean-code-dotnet/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/thangchung%2Fclean-code-dotnet/sbom","scorecard":{"id":876564,"data":{"date":"2025-08-11","repo":{"name":"github.com/thangchung/clean-code-dotnet","commit":"293203db58eece40361b249f946819277b0d915e"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.3,"checks":[{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Code-Review","score":2,"reason":"Found 5/21 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 14 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-24T06:14:17.821Z","repository_id":38375896,"created_at":"2025-08-24T06:14:17.821Z","updated_at":"2025-08-24T06:14:17.821Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278399628,"owners_count":25980330,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["aspnet","awesome","azure","best-practices","clean-architecture","clean-code","composition","csharp","dotnet","inheritance","principles","solid"],"created_at":"2024-07-31T02:00:39.975Z","updated_at":"2025-10-05T01:31:41.611Z","avatar_url":"https://github.com/thangchung.png","language":"C#","readme":"# 适用于 .NET/.NET Core 的代码整洁之道\n\n如果您喜欢 `clean-code-dotnet` 项目，或者它对于您所帮助，请给这个仓库一个小星星 :star: 。这不仅会激励我们的 .NET 社区，也会帮助全世界的 .NET 开发者提升编写优质代码的技能。非常感谢您 :+1:\n\n请查看我的[博客](https://medium.com/@thangchung)，或者在 [Twitter](https://twitter.com/thangchung) 上联系我！\n\n# 目录\n\n- [适用于 .NET/.NET Core 的代码整洁之道](#%e9%80%82%e7%94%a8%e4%ba%8e-netnet-core-%e7%9a%84%e4%bb%a3%e7%a0%81%e6%95%b4%e6%b4%81%e4%b9%8b%e9%81%93)\n- [目录](#%e7%9b%ae%e5%bd%95)\n- [介绍](#%e4%bb%8b%e7%bb%8d)\n- [.NET 中的整洁代码](#net-%e4%b8%ad%e7%9a%84%e6%95%b4%e6%b4%81%e4%bb%a3%e7%a0%81)\n  - [命名](#%e5%91%bd%e5%90%8d)\n  - [变量](#%e5%8f%98%e9%87%8f)\n  - [函数](#%e5%87%bd%e6%95%b0)\n  - [对象和数据结构](#%e5%af%b9%e8%b1%a1%e5%92%8c%e6%95%b0%e6%8d%ae%e7%bb%93%e6%9e%84)\n  - [类](#%e7%b1%bb)\n  - [SOLID](#solid)\n  - [测试](#%e6%b5%8b%e8%af%95)\n  - [并发](#%e5%b9%b6%e5%8f%91)\n  - [异常处理](#%e5%bc%82%e5%b8%b8%e5%a4%84%e7%90%86)\n  - [格式化](#%e6%a0%bc%e5%bc%8f%e5%8c%96)\n  - [注释](#%e6%b3%a8%e9%87%8a)\n- [其它关于代码整洁之道的资源](#%e5%85%b6%e5%ae%83%e5%85%b3%e4%ba%8e%e4%bb%a3%e7%a0%81%e6%95%b4%e6%b4%81%e4%b9%8b%e9%81%93%e7%9a%84%e8%b5%84%e6%ba%90)\n  - [其它代码整洁之道列表](#%e5%85%b6%e5%ae%83%e4%bb%a3%e7%a0%81%e6%95%b4%e6%b4%81%e4%b9%8b%e9%81%93%e5%88%97%e8%a1%a8)\n  - [工具](#%e5%b7%a5%e5%85%b7)\n  - [表格](#%e8%a1%a8%e6%a0%bc)\n- [贡献者](#%e8%b4%a1%e7%8c%ae%e8%80%85)\n- [支持者](#%e6%94%af%e6%8c%81%e8%80%85)\n- [赞助商](#%e8%b5%9e%e5%8a%a9%e5%95%86)\n- [许可证](#%e8%ae%b8%e5%8f%af%e8%af%81)\n\n# 介绍\n\n![Humorous image of software quality estimation as a count of how many expletives you shout when reading code](http://www.osnews.com/images/comics/wtfm.jpg)\n\n软件工程原则，是来自于 Robert C. Martin 的一本书 [_Clean Code_](https://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882), 就 .NET/.NET Core 而言，它并不是编码风格指南，而是为了指导开发者能够编写出具有可读性、可重用性和可重构的 .NET/.NET Core 程序。\n\n这里面描述的每一项原则并不是被严格遵守的，并且支持者也相对更少。这些仅仅是指导指南，但这些指南是 _Clean Code_. 作者们多年经验的智慧结晶。\n\n灵感来源于 [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) 和 [clean-code-php](https://github.com/jupeter/clean-code-php)。\n\n# .NET 中的整洁代码\n\n## 命名\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免使用随意的命名\u003c/b\u003e\u003c/summary\u003e\n\n代码中采用优雅而不随意的命名方式会易于被更多的开发者采用，命名名称应反映出它的作用及对应的上下文关系\n\n**Bad:**\n\n```csharp\nint d;\n```\n\n**Good:**\n\n```csharp\nint daySinceModification;\n```\n\n**[⬆ Back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免使用误导性名称\u003c/b\u003e\u003c/summary\u003e\n\n给变量定义的名称需要反映出该变量的用途\n\n**Bad:**\n\n```csharp\nvar dataFromDb = db.GetFromService().ToList();\n```\n\n**Good:**\n\n```csharp\nvar listOfEmployee = _employeeService.GetEmployees().ToList();\n```\n\n**[⬆ Back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免使用匈牙利命名法\u003c/b\u003e\u003c/summary\u003e\n\n匈牙利命名法会在已定义的变量加上类型前缀，这是毫无意义的，因为现代化的继承开发环境会自动标识变量类型。\n\n**Bad:**\n\n```csharp\nint iCounter;\nstring strFullName;\nDateTime dModifiedDate;\n```\n\n**Good:**\n\n```csharp\nint counter;\nstring fullName;\nDateTime modifiedDate;\n```\n匈牙利命名发也不应该用于参数命令。\n\n**Bad:**\n\n```csharp\npublic bool IsShopOpen(string pDay, int pAmount)\n{\n    // some logic\n}\n```\n\n**Good:**\n\n```csharp\npublic bool IsShopOpen(string day, int amount)\n{\n    // some logic\n}\n```\n\n**[⬆ Back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用一致的大写方式\u003c/b\u003e\u003c/summary\u003e\n\n大写式命名可以向你暴露一些变量、功能等信息。这个规则具有主观性，所以你们团队可以选择你们喜欢的方式，但只要保持一致即可。\n\n**Bad:**\n\n```csharp\nconst int DAYS_IN_WEEK = 7;\nconst int daysInMonth = 30;\n\nvar songs = new List\u003cstring\u003e { 'Back In Black', 'Stairway to Heaven', 'Hey Jude' };\nvar Artists = new List\u003cstring\u003e { 'ACDC', 'Led Zeppelin', 'The Beatles' };\n\nbool EraseDatabase() {}\nbool Restore_database() {}\n\nclass animal {}\nclass Alpaca {}\n```\n\n**Good:**\n\n```csharp\nconst int DaysInWeek = 7;\nconst int DaysInMonth = 30;\n\nvar songs = new List\u003cstring\u003e { 'Back In Black', 'Stairway to Heaven', 'Hey Jude' };\nvar artists = new List\u003cstring\u003e { 'ACDC', 'Led Zeppelin', 'The Beatles' };\n\nbool EraseDatabase() {}\nbool RestoreDatabase() {}\n\nclass Animal {}\nclass Alpaca {}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用可读的命名方式\u003c/b\u003e\u003c/summary\u003e\n\n当变量和函数的命名不可读时，研究它们的函数是需要花费一些时间的。\n\n**Bad:**\n\n```csharp\npublic class Employee\n{\n    public Datetime sWorkDate { get; set; } // what the heck is this\n    public Datetime modTime { get; set; } // same here\n}\n```\n\n**Good:**\n\n```csharp\npublic class Employee\n{\n    public Datetime StartWorkingDate { get; set; }\n    public Datetime ModificationTime { get; set; }\n}\n```\n\n**[⬆ Back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用驼峰命名法\u003c/b\u003e\u003c/summary\u003e\n\n针对变量和函数应采用 [驼峰命名法](https://en.wikipedia.org/wiki/Camel_case) \n\n**Bad:**\n\n```csharp\nvar employeephone;\n\npublic double CalculateSalary(int workingdays, int workinghours)\n{\n    // some logic\n}\n```\n\n**Good:**\n\n```csharp\nvar employeePhone;\n\npublic double CalculateSalary(int workingDays, int workingHours)\n{\n    // some logic\n}\n```\n\n**[⬆ Back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用概括性命名\u003c/b\u003e\u003c/summary\u003e\n\n那些阅读你代码的人通常也是开发者，合理组织每个页面内容的的命名，让每个人都能轻易理解你想表达式的意思，这样我们就不用花费时间去想每个人解释里面变量、函数的功能。\n\n**Good**\n\n```csharp\npublic class SingleObject\n{\n    // create an object of SingleObject\n    private static SingleObject _instance = new SingleObject();\n\n    // make the constructor private so that this class cannot be instantiated\n    private SingleObject() {}\n\n    // get the only object available\n    public static SingleObject GetInstance()\n    {\n        return _instance;\n    }\n\n    public string ShowMessage()\n    {\n        return \"Hello World!\";\n    }\n}\n\npublic static void main(String[] args)\n{\n    // illegal construct\n    // var object = new SingleObject();\n\n    // Get the only object available\n    var singletonObject = SingleObject.GetInstance();\n\n    // show the message\n    singletonObject.ShowMessage();\n}\n```\n\n**[⬆ Back to top](#目录)**\n\n\u003c/details\u003e\n\n## 变量\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免嵌套太深，及时返回\u003c/b\u003e\u003c/summary\u003e\n\n过多的 `if else` 段会让代码变得晦涩难懂，**简洁明了优于暗藏玄机**。\n\n**Bad:**\n\n```csharp\npublic bool IsShopOpen(string day)\n{\n    if (!string.IsNullOrEmpty(day))\n    {\n        day = day.ToLower();\n        if (day == \"friday\")\n        {\n            return true;\n        }\n        else if (day == \"saturday\")\n        {\n            return true;\n        }\n        else if (day == \"sunday\")\n        {\n            return true;\n        }\n        else\n        {\n            return false;\n        }\n    }\n    else\n    {\n        return false;\n    }\n\n}\n```\n\n**Good:**\n\n```csharp\npublic bool IsShopOpen(string day)\n{\n    if (string.IsNullOrEmpty(day))\n    {\n        return false;\n    }\n\n    var openingDays = new[] { \"friday\", \"saturday\", \"sunday\" };\n    return openingDays.Any(d =\u003e d == day.ToLower());\n}\n```\n\n**Bad:**\n\n```csharp\npublic long Fibonacci(int n)\n{\n    if (n \u003c 50)\n    {\n        if (n != 0)\n        {\n            if (n != 1)\n            {\n                return Fibonacci(n - 1) + Fibonacci(n - 2);\n            }\n            else\n            {\n                return 1;\n            }\n        }\n        else\n        {\n            return 0;\n        }\n    }\n    else\n    {\n        throw new System.Exception(\"Not supported\");\n    }\n}\n```\n\n**Good:**\n\n```csharp\npublic long Fibonacci(int n)\n{\n    if (n == 0)\n    {\n        return 0;\n    }\n\n    if (n == 1)\n    {\n        return 1;\n    }\n\n    if (n \u003e 50)\n    {\n        throw new System.Exception(\"Not supported\");\n    }\n\n    return Fibonacci(n - 1) + Fibonacci(n - 2);\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免主观映射\u003c/b\u003e\u003c/summary\u003e\n\n不要迫使编译器强行翻译你的代码含义 **显式优于隐式**.\n\n**Bad:**\n\n```csharp\nvar l = new[] { \"Austin\", \"New York\", \"San Francisco\" };\n\nfor (var i = 0; i \u003c l.Count(); i++)\n{\n    var li = l[i];\n    DoStuff();\n    DoSomeOtherStuff();\n\n    // ...\n    // ...\n    // ...\n    // Wait, what is `li` for again?\n    Dispatch(li);\n}\n```\n\n**Good:**\n\n```csharp\nvar locations = new[] { \"Austin\", \"New York\", \"San Francisco\" };\n\nforeach (var location in locations)\n{\n    DoStuff();\n    DoSomeOtherStuff();\n\n    // ...\n    // ...\n    // ...\n    Dispatch(location);\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免使用魔法字符串\u003c/b\u003e\u003c/summary\u003e\n\n魔法字符串是指直接在应用程序代码中指定的字符串值，这些字符串对会应用程序的行为有所影响。通常，此类字符串最终会在系统中重复使用，并且由于它们无法使用重构工具自动更新，因此当对某些字符串进行更改时，它们将成为常见的 Bug 来源，而不是其他字符串。\n\n**Bad**\n\n```csharp\nif (userRole == \"Admin\")\n{\n    // logic in here\n}\n```\n\n**Good**\n\n```csharp\nconst string ADMIN_ROLE = \"Admin\"\nif (userRole == ADMIN_ROLE)\n{\n    // logic in here\n}\n```\n\n使用这种方式的话，我们只需要改变关键的地方，其它地方也就会跟着改变。\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e不要引入不必要的上下文\u003c/b\u003e\u003c/summary\u003e\n\n如果你的类/对象名称已经告诉了你一些信息，不要在其内部定义重复定义该变量名称。\n\n**Bad:**\n\n```csharp\npublic class Car\n{\n    public string CarMake { get; set; }\n    public string CarModel { get; set; }\n    public string CarColor { get; set; }\n\n    //...\n}\n```\n\n**Good:**\n\n```csharp\npublic class Car\n{\n    public string Make { get; set; }\n    public string Model { get; set; }\n    public string Color { get; set; }\n\n    //...\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用有意义和可读的变量名称\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n```csharp\nvar ymdstr = DateTime.UtcNow.ToString(\"MMMM dd, yyyy\");\n```\n\n**Good:**\n\n```csharp\nvar currentDate = DateTime.UtcNow.ToString(\"MMMM dd, yyyy\");\n```\n\n**[⬆ Back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e对相同类型的变量使用相同的名称\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n```csharp\nGetUserInfo();\nGetUserData();\nGetUserRecord();\nGetUserProfile();\n```\n\n**Good:**\n\n```csharp\nGetUser();\n```\n\n**[⬆ Back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用可搜索的名称（第 1 部分）\u003c/b\u003e\u003c/summary\u003e\n\n我们阅读的代码比我们的写的代码要多。我们写的代码应该具有可读性和可搜索性，这个很重要。使用不合适的命令方式会影响我们对程序的理解，这会伤害到阅读者，让你的命名可搜索。\n\n**Bad:**\n\n```csharp\n// What the heck is data for?\nvar data = new { Name = \"John\", Age = 42 };\n\nvar stream1 = new MemoryStream();\nvar ser1 = new DataContractJsonSerializer(typeof(object));\nser1.WriteObject(stream1, data);\n\nstream1.Position = 0;\nvar sr1 = new StreamReader(stream1);\nConsole.Write(\"JSON form of Data object: \");\nConsole.WriteLine(sr1.ReadToEnd());\n```\n\n**Good:**\n\n```csharp\nvar person = new Person\n{\n    Name = \"John\",\n    Age = 42\n};\n\nvar stream2 = new MemoryStream();\nvar ser2 = new DataContractJsonSerializer(typeof(Person));\nser2.WriteObject(stream2, data);\n\nstream2.Position = 0;\nvar sr2 = new StreamReader(stream2);\nConsole.Write(\"JSON form of Data object: \");\nConsole.WriteLine(sr2.ReadToEnd());\n```\n\n**[⬆ Back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用可搜索的名称（第 2 部分）\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n```csharp\nvar data = new { Name = \"John\", Age = 42, PersonAccess = 4};\n\n// What the heck is 4 for?\nif (data.PersonAccess == 4)\n{\n    // do edit ...\n}\n```\n\n**Good:**\n\n```csharp\npublic enum PersonAccess : int\n{\n    ACCESS_READ = 1,\n    ACCESS_CREATE = 2,\n    ACCESS_UPDATE = 4,\n    ACCESS_DELETE = 8\n}\n\nvar person = new Person\n{\n    Name = \"John\",\n    Age = 42,\n    PersonAccess= PersonAccess.ACCESS_CREATE\n};\n\nif (person.PersonAccess == PersonAccess.ACCESS_UPDATE)\n{\n    // do edit ...\n}\n```\n\n**[⬆ Back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用解释型变量\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n```csharp\nconst string Address = \"One Infinite Loop, Cupertino 95014\";\nvar cityZipCodeRegex = @\"/^[^,\\]+[,\\\\s]+(.+?)\\s*(\\d{5})?$/\";\nvar matches = Regex.Matches(Address, cityZipCodeRegex);\nif (matches[0].Success == true \u0026\u0026 matches[1].Success == true)\n{\n    SaveCityZipCode(matches[0].Value, matches[1].Value);\n}\n```\n\n**Good:**\n\nDecrease dependence on regex by naming subpatterns.\n\n```csharp\nconst string Address = \"One Infinite Loop, Cupertino 95014\";\nvar cityZipCodeWithGroupRegex = @\"/^[^,\\]+[,\\\\s]+(?\u003ccity\u003e.+?)\\s*(?\u003czipCode\u003e\\d{5})?$/\";\nvar matchesWithGroup = Regex.Match(Address, cityZipCodeWithGroupRegex);\nvar cityGroup = matchesWithGroup.Groups[\"city\"];\nvar zipCodeGroup = matchesWithGroup.Groups[\"zipCode\"];\nif(cityGroup.Success == true \u0026\u0026 zipCodeGroup.Success == true)\n{\n    SaveCityZipCode(cityGroup.Value, zipCodeGroup.Value);\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用默认参数而不是条件判断\u003c/b\u003e\u003c/summary\u003e\n\n**Not good:**\n\n这样并不好，因为 `breweryName` 可能为 `NULL`。\n\n这种方式在之前的版本更容易理解，它能很好地控制变量的值。\n\n```csharp\npublic void CreateMicrobrewery(string name = null)\n{\n    var breweryName = !string.IsNullOrEmpty(name) ? name : \"Hipster Brew Co.\";\n    // ...\n}\n```\n\n**Good:**\n\n```csharp\npublic void CreateMicrobrewery(string breweryName = \"Hipster Brew Co.\")\n{\n    // ...\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n## 函数\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免副作用\u003c/b\u003e\u003c/summary\u003e\n\n如果函数除了获取一个值并且返回另一个值之外执行了一些操作，则会产生副作用。副作用可能是文件写入，修改一些全局变量，或者意外地向外部暴露了数据。\n\n在某些情况下，你的程序确实需要一些副作用，像上述示例一样，你可能需要文件写入，当集中执行这些操作时，并没有多个函数或类来支持写入特定文件，这时可以通过一个服务来执行这个副作用，这是唯一的一种方法。\n\n关键点是要避免一些常见的陷阱。比如没有任何结构关联的对象间的状态共享。使用任何可写入的可变数据类型，以及不确定的副作用发生的位置。如果你能意识到这一点的话，会比周围其他程序员更高兴一些。\n\n**Bad:**\n\n```csharp\n// Global variable referenced by following function.\n// If we had another function that used this name, now it'd be an array and it could break it.\nvar name = 'Ryan McDermott';\n\npublic string SplitIntoFirstAndLastName()\n{\n   return name.Split(\" \");\n}\n\nSplitIntoFirstAndLastName();\n\nConsole.PrintLine(name); // ['Ryan', 'McDermott'];\n```\n\n**Good:**\n\n```csharp\npublic string SplitIntoFirstAndLastName(string name)\n{\n    return name.Split(\" \");\n}\n\nvar name = 'Ryan McDermott';\nvar newName = SplitIntoFirstAndLastName(name);\n\nConsole.PrintLine(name); // 'Ryan McDermott';\nConsole.PrintLine(newName); // ['Ryan', 'McDermott'];\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免非条件\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n```csharp\npublic bool IsDOMNodeNotPresent(string node)\n{\n    // ...\n}\n\nif (!IsDOMNodeNotPresent(node))\n{\n    // ...\n}\n```\n\n**Good:**\n\n```csharp\npublic bool IsDOMNodePresent(string node)\n{\n    // ...\n}\n\nif (IsDOMNodePresent(node))\n{\n    // ...\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免多条件\u003c/b\u003e\u003c/summary\u003e\n\n这似乎是一个不现实的要求，第一次听到这个的时候，大多数人说，“如果没有 `if` 语句 我怎么能实现一些功能呢？” 第二个问题通常是，\"那很好，但我为什么要这么做呢？\" 答案是我们之前学到的整洁代码概念：函数应该只做有一件事，当你的类和函数具有 \"if\" 语句时，您会告诉用户您的函数执行多个事情。记住，只做一件事。\n\n**Bad:**\n\n```csharp\nclass Airplane\n{\n    // ...\n\n    public double GetCruisingAltitude()\n    {\n        switch (_type)\n        {\n            case '777':\n                return GetMaxAltitude() - GetPassengerCount();\n            case 'Air Force One':\n                return GetMaxAltitude();\n            case 'Cessna':\n                return GetMaxAltitude() - GetFuelExpenditure();\n        }\n    }\n}\n```\n\n**Good:**\n\n```csharp\ninterface IAirplane\n{\n    // ...\n\n    double GetCruisingAltitude();\n}\n\nclass Boeing777 : IAirplane\n{\n    // ...\n\n    public double GetCruisingAltitude()\n    {\n        return GetMaxAltitude() - GetPassengerCount();\n    }\n}\n\nclass AirForceOne : IAirplane\n{\n    // ...\n\n    public double GetCruisingAltitude()\n    {\n        return GetMaxAltitude();\n    }\n}\n\nclass Cessna : IAirplane\n{\n    // ...\n\n    public double GetCruisingAltitude()\n    {\n        return GetMaxAltitude() - GetFuelExpenditure();\n    }\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免类型检查（第 1 部分）\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n```csharp\npublic Path TravelToTexas(object vehicle)\n{\n    if (vehicle.GetType() == typeof(Bicycle))\n    {\n        (vehicle as Bicycle).PeddleTo(new Location(\"texas\"));\n    }\n    else if (vehicle.GetType() == typeof(Car))\n    {\n        (vehicle as Car).DriveTo(new Location(\"texas\"));\n    }\n}\n```\n\n**Good:**\n\n```csharp\npublic Path TravelToTexas(Traveler vehicle)\n{\n    vehicle.TravelTo(new Location(\"texas\"));\n}\n```\n\nor\n\n```csharp\n// pattern matching\npublic Path TravelToTexas(object vehicle)\n{\n    if (vehicle is Bicycle bicycle)\n    {\n        bicycle.PeddleTo(new Location(\"texas\"));\n    }\n    else if (vehicle is Car car)\n    {\n        car.DriveTo(new Location(\"texas\"));\n    }\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免类型检查（第 2 部分）\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n```csharp\npublic int Combine(dynamic val1, dynamic val2)\n{\n    int value;\n    if (!int.TryParse(val1, out value) || !int.TryParse(val2, out value))\n    {\n        throw new Exception('Must be of type Number');\n    }\n\n    return val1 + val2;\n}\n```\n\n**Good:**\n\n```csharp\npublic int Combine(int val1, int val2)\n{\n    return val1 + val2;\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免在方法参数中设置标志\u003c/b\u003e\u003c/summary\u003e\n\n标志指示着这个方法有更多的职责。最好的办法是单一职责原则，如果布尔参数会往函数中会添加多个职责，那么就将这个函数拆分为两个。\n\n**Bad:**\n\n```csharp\npublic void CreateFile(string name, bool temp = false)\n{\n    if (temp)\n    {\n        Touch(\"./temp/\" + name);\n    }\n    else\n    {\n        Touch(name);\n    }\n}\n```\n\n**Good:**\n\n```csharp\npublic void CreateFile(string name)\n{\n    Touch(name);\n}\n\npublic void CreateTempFile(string name)\n{\n    Touch(\"./temp/\"  + name);\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e不要编写全局函数\u003c/b\u003e\u003c/summary\u003e\n\n\u003e 还没完\n\n在很多语言中，污染全局是一种差的实践方式，因为你可能会与其它库发送冲突，并且你的 API 用户在生产环境下获取一个异常将毫不明智。让我们一起思考一个示例：如果想要配置数组该如何处理。你可以编写一个像 `Config()` 的全局函数，但它可能会与另一个尝试执行相同操作的库发生冲突。\n\n**Bad:**\n\n```csharp\npublic string[] Config()\n{\n    return  [\n        \"foo\" =\u003e \"bar\",\n    ]\n}\n```\n\n**Good:**\n\n```csharp\nclass Configuration\n{\n    private string[] _configuration = [];\n\n    public Configuration(string[] configuration)\n    {\n        _configuration = configuration;\n    }\n\n    public string[] Get(string key)\n    {\n        return (_configuration[key]!= null) ? _configuration[key] : null;\n    }\n}\n```\n\n加载配置并创建配置实例 `Configuration`\n\n```csharp\nvar configuration = new Configuration(new string[] {\n    \"foo\" =\u003e \"bar\",\n});\n```\n\n你现在在应用程序中必须使用 `Configuration` 的实例\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e不要使用单例模式\u003c/b\u003e\u003c/summary\u003e\n\n单例模式是一种 [反模式](https://en.wikipedia.org/wiki/Singleton_pattern). 根据 from Brian Button 的描述:\n\n1. 它们通常作为一个 **全局实例** 存在，为什么这样不好？因为你在你的程序代码中 **隐藏依赖项**，而不是通过接口来暴露它们，为了避免对象传递而将其设置为全局的方式是一种 [code smell](https://en.wikipedia.org/wiki/Code_smell)。\n2. 它们违反了[单一职责原则](#single-responsibility-principle-srp)：**它们控制了自己的对象创建和生命周期**\n3. 它们本质上会导致代码紧密地 [耦合](https://en.wikipedia.org/wiki/Coupling_%28computer_programming%29)，这使得在许多情况下，在测试环境下模拟它们异常困难。\n4. 它们在应用程序的生存期内会携带状态。另一点需要测试，因为[你最终可能会得到一种情况，即测试需要排序]，这违背了单元测试的原则。为什么？因为每个单元测试相互独立。\n\n这儿也有一些 [Misko Hevery](http://misko.hevery.com/about/)  关于 [root of problem](http://misko.hevery.com/2008/08/25/root-cause-of-singletons/) 很不错的想法。\n\n**Bad:**\n\n```csharp\nclass DBConnection\n{\n    private static DBConnection _instance;\n\n    private DBConnection()\n    {\n        // ...\n    }\n\n    public static GetInstance()\n    {\n        if (_instance == null)\n        {\n            _instance = new DBConnection();\n        }\n\n        return _instance;\n    }\n\n    // ...\n}\n\nvar singleton = DBConnection.GetInstance();\n```\n\n**Good:**\n\n```csharp\nclass DBConnection\n{\n    public DBConnection(IOptions\u003cDbConnectionOption\u003e options)\n    {\n        // ...\n    }\n\n    // ...\n}\n```\n\n创建一个 `DBConnection` 实例，并通过 [Option pattern](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.1) 来进行配置\n\n```csharp\nvar options = \u003cresolve from IOC\u003e;\nvar connection = new DBConnection(options);\n```\n\n现在，你在你的应用程序中必须使用 `DBConnection` 的类型实例\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e函数参数（2个或者更少最佳）\u003c/b\u003e\u003c/summary\u003e\n\n限制函数参数的数量非常重要，因为它使测试函数变得更加容易。拥有三个以上会导致组合爆炸，您必须使用每个单独的参数测试大量不同用例。\n\n无参是理想的情况。一个或两个参数是可以的，三个应该避免，超过的话应该合并。通常，如果您有两个以上参数，则函数尝试执行的操作太多。大多数时候，一个更高级别的对象将足以作为一个参数。\n\n**Bad:**\n\n```csharp\npublic void CreateMenu(string title, string body, string buttonText, bool cancellable)\n{\n    // ...\n}\n```\n\n**Good:**\n\n```csharp\npublic class MenuConfig\n{\n    public string Title { get; set; }\n    public string Body { get; set; }\n    public string ButtonText { get; set; }\n    public bool Cancellable { get; set; }\n}\n\nvar config = new MenuConfig\n{\n    Title = \"Foo\",\n    Body = \"Bar\",\n    ButtonText = \"Baz\",\n    Cancellable = true\n};\n\npublic void CreateMenu(MenuConfig config)\n{\n    // ...\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e一个函数只应该做一件事情\u003c/b\u003e\u003c/summary\u003e\n\n在软件开发过程中，这是一个很重要的原则。当函数要做的事情超过一件的时候就很难组合到一起进行测试，这是因为，当你可以将一个函数隔离为一个操作时，可以轻松的进行重构，并且能够过得更多清晰明确的信息。如果你在这份指南中只学会到了这一点，那么你将比其他开发者更领先一些。\n\n**Bad:**\n\n```csharp\npublic void SendEmailToListOfClients(string[] clients)\n{\n    foreach (var client in clients)\n    {\n        var clientRecord = db.Find(client);\n        if (clientRecord.IsActive())\n        {\n            Email(client);\n        }\n    }\n}\n```\n\n**Good:**\n\n```csharp\npublic void SendEmailToListOfClients(string[] clients)\n{\n    var activeClients = GetActiveClients(clients);\n    // Do some logic\n}\n\npublic List\u003cClient\u003e GetActiveClients(string[] clients)\n{\n    return db.Find(clients).Where(s =\u003e s.Status == \"Active\");\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e函数命名要见名知义\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n```csharp\npublic class Email\n{\n    //...\n\n    public void Handle()\n    {\n        SendMail(this._to, this._subject, this._body);\n    }\n}\n\nvar message = new Email(...);\n// What is this? A handle for the message? Are we writing to a file now?\nmessage.Handle();\n```\n\n**Good:**\n\n```csharp\npublic class Email\n{\n    //...\n\n    public void Send()\n    {\n        SendMail(this._to, this._subject, this._body);\n    }\n}\n\nvar message = new Email(...);\n// Clear and obvious\nmessage.Send();\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e函数应该只包含一层抽象\u003c/b\u003e\u003c/summary\u003e\n\n\u003e 还没完\n\n通常情况下，当你的函数中包含超过一层的抽象表明这个函数做的事情太多了，拆分为多个函数可以提高重用性和更易于测试。\n\n**Bad:**\n\n```csharp\npublic string ParseBetterJSAlternative(string code)\n{\n    var regexes = [\n        // ...\n    ];\n\n    var statements = explode(\" \", code);\n    var tokens = new string[] {};\n    foreach (var regex in regexes)\n    {\n        foreach (var statement in statements)\n        {\n            // ...\n        }\n    }\n\n    var ast = new string[] {};\n    foreach (var token in tokens)\n    {\n        // lex...\n    }\n\n    foreach (var node in ast)\n    {\n        // parse...\n    }\n}\n```\n\n**Bad too:**\n\n我们已经执行了一些操作，但是 `ParseBetterJSAlternative()` 函数依旧很复杂，且不易于测试。\n\n```csharp\npublic string Tokenize(string code)\n{\n    var regexes = new string[]\n    {\n        // ...\n    };\n\n    var statements = explode(\" \", code);\n    var tokens = new string[] {};\n    foreach (var regex in regexes)\n    {\n        foreach (var statement in statements)\n        {\n            tokens[] = /* ... */;\n        }\n    }\n\n    return tokens;\n}\n\npublic string Lexer(string[] tokens)\n{\n    var ast = new string[] {};\n    foreach (var token in tokens)\n    {\n        ast[] = /* ... */;\n    }\n\n    return ast;\n}\n\npublic string ParseBetterJSAlternative(string code)\n{\n    var tokens = Tokenize(code);\n    var ast = Lexer(tokens);\n    foreach (var node in ast)\n    {\n        // parse...\n    }\n}\n```\n\n**Good:**\n\n最好的解决方案是分解 `ParseBetterJSAlternative()` 函数内部的所有依赖性。\n\n```csharp\nclass Tokenizer\n{\n    public string Tokenize(string code)\n    {\n        var regexes = new string[] {\n            // ...\n        };\n\n        var statements = explode(\" \", code);\n        var tokens = new string[] {};\n        foreach (var regex in regexes)\n        {\n            foreach (var statement in statements)\n            {\n                tokens[] = /* ... */;\n            }\n        }\n\n        return tokens;\n    }\n}\n\nclass Lexer\n{\n    public string Lexify(string[] tokens)\n    {\n        var ast = new[] {};\n        foreach (var token in tokens)\n        {\n            ast[] = /* ... */;\n        }\n\n        return ast;\n    }\n}\n\nclass BetterJSAlternative\n{\n    private string _tokenizer;\n    private string _lexer;\n\n    public BetterJSAlternative(Tokenizer tokenizer, Lexer lexer)\n    {\n        _tokenizer = tokenizer;\n        _lexer = lexer;\n    }\n\n    public string Parse(string code)\n    {\n        var tokens = _tokenizer.Tokenize(code);\n        var ast = _lexer.Lexify(tokens);\n        foreach (var node in ast)\n        {\n            // parse...\n        }\n    }\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e函数调用方和被调用方应该位置相近\u003c/b\u003e\u003c/summary\u003e\n\n如果一个函数调用了其它函数，请保持这些函数顺序位于同一个源代码文件中。理想情况下，让被调用者位于调用者上方。我们倾向于像读报纸一样从上到下来阅读代码。因此，请以这种阅读方式来布局代码。\n\n**Bad:**\n\n```csharp\nclass PerformanceReview\n{\n    private readonly Employee _employee;\n\n    public PerformanceReview(Employee employee)\n    {\n        _employee = employee;\n    }\n\n    private IEnumerable\u003cPeersData\u003e LookupPeers()\n    {\n        return db.lookup(_employee, 'peers');\n    }\n\n    private ManagerData LookupManager()\n    {\n        return db.lookup(_employee, 'manager');\n    }\n\n    private IEnumerable\u003cPeerReviews\u003e GetPeerReviews()\n    {\n        var peers = LookupPeers();\n        // ...\n    }\n\n    public PerfReviewData PerfReview()\n    {\n        GetPeerReviews();\n        GetManagerReview();\n        GetSelfReview();\n    }\n\n    public ManagerData GetManagerReview()\n    {\n        var manager = LookupManager();\n    }\n\n    public EmployeeData GetSelfReview()\n    {\n        // ...\n    }\n}\n\nvar  review = new PerformanceReview(employee);\nreview.PerfReview();\n```\n\n**Good:**\n\n```csharp\nclass PerformanceReview\n{\n    private readonly Employee _employee;\n\n    public PerformanceReview(Employee employee)\n    {\n        _employee = employee;\n    }\n\n    public PerfReviewData PerfReview()\n    {\n        GetPeerReviews();\n        GetManagerReview();\n        GetSelfReview();\n    }\n\n    private IEnumerable\u003cPeerReviews\u003e GetPeerReviews()\n    {\n        var peers = LookupPeers();\n        // ...\n    }\n\n    private IEnumerable\u003cPeersData\u003e LookupPeers()\n    {\n        return db.lookup(_employee, 'peers');\n    }\n\n    private ManagerData GetManagerReview()\n    {\n        var manager = LookupManager();\n        return manager;\n    }\n\n    private ManagerData LookupManager()\n    {\n        return db.lookup(_employee, 'manager');\n    }\n\n    private EmployeeData GetSelfReview()\n    {\n        // ...\n    }\n}\n\nvar review = new PerformanceReview(employee);\nreview.PerfReview();\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e封装条件\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n```csharp\nif (article.state == \"published\")\n{\n    // ...\n}\n```\n\n**Good:**\n\n```csharp\nif (article.IsPublished())\n{\n    // ...\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e移除废弃代码\u003c/b\u003e\u003c/summary\u003e\n\n废弃代码和重复代码一样糟糕，毫无疑问不应该让其存在于你的代码库中。如果它不会被调用，那就删除它！如果你仍然需要它的话，它可以安全的存在于你的版本控制中。\n\n**Bad:**\n\n```csharp\npublic void OldRequestModule(string url)\n{\n    // ...\n}\n\npublic void NewRequestModule(string url)\n{\n    // ...\n}\n\nvar request = NewRequestModule(requestUrl);\nInventoryTracker(\"apples\", request, \"www.inventory-awesome.io\");\n```\n\n**Good:**\n\n```csharp\npublic void RequestModule(string url)\n{\n    // ...\n}\n\nvar request = RequestModule(requestUrl);\nInventoryTracker(\"apples\", request, \"www.inventory-awesome.io\");\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n## 对象和数据结构\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用 getters 和 setters\u003c/b\u003e\u003c/summary\u003e\n\n在 C# / VB.NET 中，你可以为方法添加 `public`, `protected` 和 `private` 关键字。通过使用这些关键字，你可以控制对象的一些成员的访问权限。\n\n- 当你尝试通过一个对象属性来进行更多的操作，你不得不在你的代码中查找和修改它们的访问权限。\n- 通过使用 `set` 关键字可以让属性验证变得更简单。\n- 封装内部的展现形式。\n- 当进行 getting 和 setting 操作时可以更容易的添加日志和异常处理。\n- 基础基类后，你可以重写默认方法。\n- 如果是从服务器获取一个对象，你可以使用懒加载来处理对象的属性。\n\n此外，在面向对象设计原则中，这也是开闭原则的一部分。\n\n**Bad:**\n\n```csharp\nclass BankAccount\n{\n    public double Balance = 1000;\n}\n\nvar bankAccount = new BankAccount();\n\n// Fake buy shoes...\nbankAccount.Balance -= 100;\n```\n\n**Good:**\n\n```csharp\nclass BankAccount\n{\n    private double _balance = 0.0D;\n\n    pubic double Balance {\n        get {\n            return _balance;\n        }\n    }\n\n    public BankAccount(balance = 1000)\n    {\n       _balance = balance;\n    }\n\n    public void WithdrawBalance(int amount)\n    {\n        if (amount \u003e _balance)\n        {\n            throw new Exception('Amount greater than available balance.');\n        }\n\n        _balance -= amount;\n    }\n\n    public void DepositBalance(int amount)\n    {\n        _balance += amount;\n    }\n}\n\nvar bankAccount = new BankAccount();\n\n// Buy shoes...\nbankAccount.WithdrawBalance(price);\n\n// Get balance\nbalance = bankAccount.Balance;\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e让对象具有私有/受保护的成员\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n```csharp\nclass Employee\n{\n    public string Name { get; set; }\n\n    public Employee(name)\n    {\n        Name = name;\n    }\n}\n\nvar employee = new Employee('John Doe');\nConsole.WriteLine(employee.Name) // Employee name: John Doe\n```\n\n**Good:**\n\n```csharp\nclass Employee\n{\n    public string Name { get; }\n\n    public Employee(string name)\n    {\n        Name = name;\n    }\n}\n\nvar employee = new Employee('John Doe');\nConsole.WriteLine(employee.GetName());// Employee name: John Doe\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n## 类\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用链式方法\u003c/b\u003e\u003c/summary\u003e\n\n在一些类库中，这种模式是很有用且很常见的操作。它可以让你的代码以一种表达式的方式来呈现，更加简洁。因此，使用链式方法可以让你的代码看上去更加简洁。\n\n**Good:**\n\n```csharp\npublic static class ListExtensions\n{\n    public static List\u003cT\u003e FluentAdd\u003cT\u003e(this List\u003cT\u003e list, T item)\n    {\n        list.Add(item);\n        return list;\n    }\n\n    public static List\u003cT\u003e FluentClear\u003cT\u003e(this List\u003cT\u003e list)\n    {\n        list.Clear();\n        return list;\n    }\n\n    public static List\u003cT\u003e FluentForEach\u003cT\u003e(this List\u003cT\u003e list, Action\u003cT\u003e action)\n    {\n        list.ForEach(action);\n        return list;\n    }\n\n    public static List\u003cT\u003e FluentInsert\u003cT\u003e(this List\u003cT\u003e list, int index, T item)\n    {\n        list.Insert(index, item);\n        return list;\n    }\n\n    public static List\u003cT\u003e FluentRemoveAt\u003cT\u003e(this List\u003cT\u003e list, int index)\n    {\n        list.RemoveAt(index);\n        return list;\n    }\n\n    public static List\u003cT\u003e FluentReverse\u003cT\u003e(this List\u003cT\u003e list)\n    {\n        list.Reverse();\n        return list;\n    }\n}\n\ninternal static void ListFluentExtensions()\n{\n    var list = new List\u003cint\u003e() { 1, 2, 3, 4, 5 }\n        .FluentAdd(1)\n        .FluentInsert(0, 0)\n        .FluentRemoveAt(1)\n        .FluentReverse()\n        .FluentForEach(value =\u003e value.WriteLine())\n        .FluentClear();\n}\n```\n\n**[⬆ back to top](#table-of-contents)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e组合优于继承\u003c/b\u003e\u003c/summary\u003e\n\n正如由 Gang of Four 编写的著作 [_Design Patterns_](https://en.wikipedia.org/wiki/Design_Patterns) 里面所述。如果可以选择的话，你应该倾向于使用组合而不是继承。这里面有很多不错的原因来论述使用继承和组合。\n\n关于这一论点的主要观点是如果你本能地尝试使用继承，那么考虑一下组合是否能更好的解决你的问题，在某些情况下，它确实可以。\n\n你可能接着会疑惑，\"我应该什么时候使用继承？\" 这取决你你怎么解决问题，这里有一个不错的列表来指导你在什么情况下使用继承更有意义，而不是组合。\n\n1. 你的继承是为了表达一种 \"是 A\" 的关系而不是 \"有 A\" 的关系 (人类-\u003e动物 vs. 用户-\u003e用户详情).\n2. 你可以从基类重用代码 (人类像所有动物一样可以移动)。\n3. 您希望通过更改基类对派生类进行全局更改 (改变所有动物移动时的热量消耗。)\n\n**Bad:**\n\n```csharp\nclass Employee\n{\n    private string Name { get; set; }\n    private string Email { get; set; }\n\n    public Employee(string name, string email)\n    {\n        Name = name;\n        Email = email;\n    }\n\n    // ...\n}\n\n// Bad because Employees \"have\" tax data.\n// EmployeeTaxData is not a type of Employee\n\nclass EmployeeTaxData : Employee\n{\n    private string Name { get; }\n    private string Email { get; }\n\n    public EmployeeTaxData(string name, string email, string ssn, string salary)\n    {\n         // ...\n    }\n\n    // ...\n}\n```\n\n**Good:**\n\n```csharp\nclass EmployeeTaxData\n{\n    public string Ssn { get; }\n    public string Salary { get; }\n\n    public EmployeeTaxData(string ssn, string salary)\n    {\n        Ssn = ssn;\n        Salary = salary;\n    }\n\n    // ...\n}\n\nclass Employee\n{\n    public string Name { get; }\n    public string Email { get; }\n    public EmployeeTaxData TaxData { get; }\n\n    public Employee(string name, string email)\n    {\n        Name = name;\n        Email = email;\n    }\n\n    public void SetTax(string ssn, double salary)\n    {\n        TaxData = new EmployeeTaxData(ssn, salary);\n    }\n\n    // ...\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n## SOLID\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e什么是 SOLID?\u003c/b\u003e\u003c/summary\u003e\n\n**SOLID** 是 Michael Feathers 为  Robert Martin 命名的前五个原则引入的首字母简称。它指导者面向对象编程和设计的五个基本原则。\n\n- [S: 单一职责原则 (SRP)](#single-responsibility-principle-srp)\n- [O: 开/闭原则 (OCP)](#openclosed-principle-ocp)\n- [L: 里氏替换原则 (LSP)](#liskov-substitution-principle-lsp)\n- [I: 接口隔离原则 (ISP)](#interface-segregation-principle-isp)\n- [D: 依赖倒置原则 (DIP)](#dependency-inversion-principle-dip)\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e单一职责原则 (SRP)\u003c/b\u003e\u003c/summary\u003e\n\n如代码整洁之道中所述的那样，\"类更改的原因永远不应该超过一个\"。一个功能繁多的类似乎很有诱惑力，像你登机时只能携带一个手提箱，但问题在于你的类不会具有凝聚力，它会被赋予许多可以改变的理由。尽量减少更改类所需要的时间很重要。\n\n这很重要，因为如果一个类中包含太多功能，并且更改了部分，这会导致很难理解这些更改会怎样影响到代码库中其它的依赖项。\n\n**Bad:**\n\n```csharp\nclass UserSettings\n{\n    private User User;\n\n    public UserSettings(User user)\n    {\n        User = user;\n    }\n\n    public void ChangeSettings(Settings settings)\n    {\n        if (verifyCredentials())\n        {\n            // ...\n        }\n    }\n\n    private bool VerifyCredentials()\n    {\n        // ...\n    }\n}\n```\n\n**Good:**\n\n```csharp\nclass UserAuth\n{\n    private User User;\n\n    public UserAuth(User user)\n    {\n        User = user;\n    }\n\n    public bool VerifyCredentials()\n    {\n        // ...\n    }\n}\n\nclass UserSettings\n{\n    private User User;\n    private UserAuth Auth;\n\n    public UserSettings(User user)\n    {\n        User = user;\n        Auth = new UserAuth(user);\n    }\n\n    public void ChangeSettings(Settings settings)\n    {\n        if (Auth.VerifyCredentials())\n        {\n            // ...\n        }\n    }\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e开/闭原则 (OCP)\u003c/b\u003e\u003c/summary\u003e\n\n正如 Bertrand Meyer 所述的那样，\"软件实体 (类，模块，函数等) 应该对扩展开放，对修改关闭。\" 这想表达什么呢？这个原则最基本的要求是你应该允许用户在不更改现有代码的前提下可以添加新功能。\n\n**Bad:**\n\n```csharp\nabstract class AdapterBase\n{\n    protected string Name;\n\n    public string GetName()\n    {\n        return Name;\n    }\n}\n\nclass AjaxAdapter : AdapterBase\n{\n    public AjaxAdapter()\n    {\n        Name = \"ajaxAdapter\";\n    }\n}\n\nclass NodeAdapter : AdapterBase\n{\n    public NodeAdapter()\n    {\n        Name = \"nodeAdapter\";\n    }\n}\n\nclass HttpRequester : AdapterBase\n{\n    private readonly AdapterBase Adapter;\n\n    public HttpRequester(AdapterBase adapter)\n    {\n        Adapter = adapter;\n    }\n\n    public bool Fetch(string url)\n    {\n        var adapterName = Adapter.GetName();\n\n        if (adapterName == \"ajaxAdapter\")\n        {\n            return MakeAjaxCall(url);\n        }\n        else if (adapterName == \"httpNodeAdapter\")\n        {\n            return MakeHttpCall(url);\n        }\n    }\n\n    private bool MakeAjaxCall(string url)\n    {\n        // request and return promise\n    }\n\n    private bool MakeHttpCall(string url)\n    {\n        // request and return promise\n    }\n}\n```\n\n**Good:**\n\n```csharp\ninterface IAdapter\n{\n    bool Request(string url);\n}\n\nclass AjaxAdapter : IAdapter\n{\n    public bool Request(string url)\n    {\n        // request and return promise\n    }\n}\n\nclass NodeAdapter : IAdapter\n{\n    public bool Request(string url)\n    {\n        // request and return promise\n    }\n}\n\nclass HttpRequester\n{\n    private readonly IAdapter Adapter;\n\n    public HttpRequester(IAdapter adapter)\n    {\n        Adapter = adapter;\n    }\n\n    public bool Fetch(string url)\n    {\n        return Adapter.Request(url);\n    }\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e里氏替换原则 (LSP)\u003c/b\u003e\u003c/summary\u003e\n\n对于一个很简单的概念来说，这是很抽象的。它的准确表述是 \"如果 S 是 T 的子类，那么类型 T 的对象可以转化为类型 S 的对象，即在无需修改程序（正确性，任务执行等）的情况下 在类型为 S 的对象可以替换类型为 T 的对象。\" 这是一个相对可怕的定义。\n\n对这一原则最好的解释是如果你有一个父类和一个子类，那么基类和子类可以相互使用，而不会得到错误答案，这可能仍然令人困惑。在数学上，正方形是一个矩形，但是使用 \"is-a\" 关系通过继承对它建模，你很快就会陷入麻烦。\n\n**Bad:**\n\n```csharp\nclass Rectangle\n{\n    protected double Width = 0;\n    protected double Height = 0;\n\n    public Drawable Render(double area)\n    {\n        // ...\n    }\n\n    public void SetWidth(double width)\n    {\n        Width = width;\n    }\n\n    public void SetHeight(double height)\n    {\n        Height = height;\n    }\n\n    public double GetArea()\n    {\n        return Width * Height;\n    }\n}\n\nclass Square : Rectangle\n{\n    public double SetWidth(double width)\n    {\n        Width = Height = width;\n    }\n\n    public double SetHeight(double height)\n    {\n        Width = Height = height;\n    }\n}\n\nDrawable RenderLargeRectangles(Rectangle rectangles)\n{\n    foreach (rectangle in rectangles)\n    {\n        rectangle.SetWidth(4);\n        rectangle.SetHeight(5);\n        var area = rectangle.GetArea(); // BAD: Will return 25 for Square. Should be 20.\n        rectangle.Render(area);\n    }\n}\n\nvar rectangles = new[] { new Rectangle(), new Rectangle(), new Square() };\nRenderLargeRectangles(rectangles);\n```\n\n**Good:**\n\n```csharp\nabstract class ShapeBase\n{\n    protected double Width = 0;\n    protected double Height = 0;\n\n    abstract public double GetArea();\n\n    public Drawable Render(double area)\n    {\n        // ...\n    }\n}\n\nclass Rectangle : ShapeBase\n{\n    public void SetWidth(double width)\n    {\n        Width = width;\n    }\n\n    public void SetHeight(double height)\n    {\n        Height = height;\n    }\n\n    public double GetArea()\n    {\n        return Width * Height;\n    }\n}\n\nclass Square : ShapeBase\n{\n    private double Length = 0;\n\n    public double SetLength(double length)\n    {\n        Length = length;\n    }\n\n    public double GetArea()\n    {\n        return Math.Pow(Length, 2);\n    }\n}\n\nDrawable RenderLargeRectangles(Rectangle rectangles)\n{\n    foreach (rectangle in rectangles)\n    {\n        if (rectangle is Square)\n        {\n            rectangle.SetLength(5);\n        }\n        else if (rectangle is Rectangle)\n        {\n            rectangle.SetWidth(4);\n            rectangle.SetHeight(5);\n        }\n\n        var area = rectangle.GetArea();\n        rectangle.Render(area);\n    }\n}\n\nvar shapes = new[] { new Rectangle(), new Rectangle(), new Square() };\nRenderLargeRectangles(shapes);\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e接口隔离原则 (ISP)\u003c/b\u003e\u003c/summary\u003e\n\nISP 指出 \"不应该强迫客户端依赖于它不使用的接口。\"\n\n一个很好的例子可以很好地证明这一点。比如需要大量的设置对象，不需要客户端创建很多的设置项是正确的，因为大多数情况下，它们并不需要所有的设置项，使它们作为可选项有助于防止出现 \"胖接口\"。\n\n**Bad:**\n\n```csharp\npublic interface IEmployee\n{\n    void Work();\n    void Eat();\n}\n\npublic class Human : IEmployee\n{\n    public void Work()\n    {\n        // ....working\n    }\n\n    public void Eat()\n    {\n        // ...... eating in lunch break\n    }\n}\n\npublic class Robot : IEmployee\n{\n    public void Work()\n    {\n        //.... working much more\n    }\n\n    public void Eat()\n    {\n        //.... robot can't eat, but it must implement this method\n    }\n}\n```\n\n**Good:**\n\nNot every worker is an employee, but every employee is an worker.\n\n```csharp\npublic interface IWorkable\n{\n    void Work();\n}\n\npublic interface IFeedable\n{\n    void Eat();\n}\n\npublic interface IEmployee : IFeedable, IWorkable\n{\n}\n\npublic class Human : IEmployee\n{\n    public void Work()\n    {\n        // ....working\n    }\n\n    public void Eat()\n    {\n        //.... eating in lunch break\n    }\n}\n\n// robot can only work\npublic class Robot : IWorkable\n{\n    public void Work()\n    {\n        // ....working\n    }\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e依赖倒置原则 (DIP)\u003c/b\u003e\u003c/summary\u003e\n\n这一原则指出两个基本点：\n\n1. 高级别模块不应该依赖于低级别模块，它们都应该依赖于抽象层。\n2. 抽象不应该依赖于细节，细节应该依赖于抽象。\n\n这一点最初很难理解。但如果你已经使用 .NET/.NET Core framework，你应该已经看过 [Dependency Injection](https://martinfowler.com/articles/injection.html) (DI) 对这一原则的实现。虽然它们不是相同的概念，但 DIP 使高级模块无法了解低级模块的详细信息，也无法设置。这可以通过 DI 来实现，这样做的最大好处是减少了模块间的耦合，耦合是一种非常非常糟糕的开发模式，它会导致代码难以重构。\n\n**Bad:**\n\n```csharp\npublic abstract class EmployeeBase\n{\n    protected virtual void Work()\n    {\n        // ....working\n    }\n}\n\npublic class Human : EmployeeBase\n{\n    public override void Work()\n    {\n        //.... working much more\n    }\n}\n\npublic class Robot : EmployeeBase\n{\n    public override void Work()\n    {\n        //.... working much, much more\n    }\n}\n\npublic class Manager\n{\n    private readonly Robot _robot;\n    private readonly Human _human;\n\n    public Manager(Robot robot, Human human)\n    {\n        _robot = robot;\n        _human = human;\n    }\n\n    public void Manage()\n    {\n        _robot.Work();\n        _human.Work();\n    }\n}\n```\n\n**Good:**\n\n```csharp\npublic interface IEmployee\n{\n    void Work();\n}\n\npublic class Human : IEmployee\n{\n    public void Work()\n    {\n        // ....working\n    }\n}\n\npublic class Robot : IEmployee\n{\n    public void Work()\n    {\n        //.... working much more\n    }\n}\n\npublic class Manager\n{\n    private readonly IEnumerable\u003cIEmployee\u003e _employees;\n\n    public Manager(IEnumerable\u003cIEmployee\u003e employees)\n    {\n        _employees = employees;\n    }\n\n    public void Manage()\n    {\n        foreach (var employee in _employees)\n        {\n            _employee.Work();\n        }\n    }\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e不要重复你自己 (DRY)\u003c/b\u003e\u003c/summary\u003e\n\n尝试了解  [DRY](https://en.wikipedia.org/wiki/Don%27t_repeat_yourself) 原则\n\n尽你所能去避免重复的代码，重复代码不好，因为这意味着如果你修改某些逻辑，那么你需要在很多地方修改这些逻辑。\n\n想象一下，如果你经营一家餐馆，并跟踪你的库存：所有的西红柿，洋葱，大蒜，香料等。如果您有多份列表，当你使用了菜与西红柿，你更新了这份列表，那么其它列表都需要更新。如果您只有一份列表，则只需要更新一个位置即可！\n\n通常，你有重复的代码是由于你有两个或者更多略有差异的地方，它们有很多共同点，但它们的不同点迫使你不得不视图两个或多个单独的函数，这些函数也会执行许多相同的操作。删除重复的代码意味着创建一个抽象，只需一个函数/模块/类即可处理这组不同的东西。\n\n正确抽象至关重要，这就是为什么你应该遵循 [Classes]（#classes） 一节中阐述的 SOLID 原则的原因。错误的抽象可能比重复的代码更糟糕，所以要小心！话虽如此，如果你能做出一个良好的抽象，做到这一点！不要重复自己，否则你当你想做一处修改的时候会发现自己需要更新多个地方。\n\n**Bad:**\n\n```csharp\npublic List\u003cEmployeeData\u003e ShowDeveloperList(Developers developers)\n{\n    foreach (var developers in developer)\n    {\n        var expectedSalary = developer.CalculateExpectedSalary();\n        var experience = developer.GetExperience();\n        var githubLink = developer.GetGithubLink();\n        var data = new[] {\n            expectedSalary,\n            experience,\n            githubLink\n        };\n\n        Render(data);\n    }\n}\n\npublic List\u003cManagerData\u003e ShowManagerList(Manager managers)\n{\n    foreach (var manager in managers)\n    {\n        var expectedSalary = manager.CalculateExpectedSalary();\n        var experience = manager.GetExperience();\n        var githubLink = manager.GetGithubLink();\n        var data =\n        new[] {\n            expectedSalary,\n            experience,\n            githubLink\n        };\n\n        render(data);\n    }\n}\n```\n\n**Good:**\n\n```csharp\npublic List\u003cEmployeeData\u003e ShowList(Employee employees)\n{\n    foreach (var employee in employees)\n    {\n        var expectedSalary = employees.CalculateExpectedSalary();\n        var experience = employees.GetExperience();\n        var githubLink = employees.GetGithubLink();\n        var data =\n        new[] {\n            expectedSalary,\n            experience,\n            githubLink\n        };\n\n        render(data);\n    }\n}\n```\n\n**Very good:**\n\nIt is better to use a compact version of the code.\n\n```csharp\npublic List\u003cEmployeeData\u003e ShowList(Employee employees)\n{\n    foreach (var employee in employees)\n    {\n        render(new[] {\n            employee.CalculateExpectedSalary(),\n            employee.GetExperience(),\n            employee.GetGithubLink()\n        });\n    }\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003c/details\u003e\n\n## 测试\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e测试的基本概念\u003c/b\u003e\u003c/summary\u003e\n\n测试比开发更重要，如果你没有测试或者测试量不够，那么每次发布的时候，你不能确保你没有引入新的 BUG，花费的金额数量取决于你的团队，但拥有 100% 的覆盖率（所有条件和分支）是你实现非常高的可信度和让开发人员安心的方式。这意味着除了拥有一个优秀的测试框架外，你也需要使用 [良好的覆盖工具](https://docs.microsoft.com/en-us/visualstudio/test/using-code-coverage-to-determine-how-much-code-is-being-tested).\n\n没有理由不写测试，这里有 [大量的优秀 .NET 测试框架](https://github.com/thangchung/awesome-dotnet-core#testing)，选择一款你所在团队喜欢的框架。当你找到一款适合你团队使用的的测试框架时，其目的是始终为你介绍的每个新功能/模块编写测试。如果你的首选方法是测试驱动开发 （TDD），那很好，但主要目的就是确保在启动任何功能或重构现有功能之前达到覆盖目标。\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e每个测试的单一概念\u003c/b\u003e\u003c/summary\u003e\n\n确保你的测试以点为中心，而不是杂乱（不相关）内容，强制使用 [AAA 模式](http://wiki.c2.com/?ArrangeActAssert) 可以然你的代码更加整洁和易读。\n\n**Bad:**\n\n```csharp\n\npublic class MakeDotNetGreatAgainTests\n{\n    [Fact]\n    public void HandleDateBoundaries()\n    {\n        var date = new MyDateTime(\"1/1/2015\");\n        date.AddDays(30);\n        Assert.Equal(\"1/31/2015\", date);\n\n        date = new MyDateTime(\"2/1/2016\");\n        date.AddDays(28);\n        Assert.Equal(\"02/29/2016\", date);\n\n        date = new MyDateTime(\"2/1/2015\");\n        date.AddDays(28);\n        Assert.Equal(\"03/01/2015\", date);\n    }\n}\n\n```\n\n**Good:**\n\n```csharp\n\npublic class MakeDotNetGreatAgainTests\n{\n    [Fact]\n    public void Handle30DayMonths()\n    {\n        // Arrange\n        var date = new MyDateTime(\"1/1/2015\");\n\n        // Act\n        date.AddDays(30);\n\n        // Assert\n        Assert.Equal(\"1/31/2015\", date);\n    }\n\n    [Fact]\n    public void HandleLeapYear()\n    {\n        // Arrange\n        var date = new MyDateTime(\"2/1/2016\");\n\n        // Act\n        date.AddDays(28);\n\n        // Assert\n        Assert.Equal(\"02/29/2016\", date);\n    }\n\n    [Fact]\n    public void HandleNonLeapYear()\n    {\n        // Arrange\n        var date = new MyDateTime(\"2/1/2015\");\n\n        // Act\n        date.AddDays(28);\n\n        // Assert\n        Assert.Equal(\"03/01/2015\", date);\n    }\n}\n\n```\n\n\u003e Soure https://www.codingblocks.net/podcast/how-to-write-amazing-unit-tests\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n## 并发\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用 Async/Await\u003c/b\u003e\u003c/summary\u003e\n\n**异步编程指南摘要**\n\n| Name              | Description                                       | Exceptions                      |\n| ----------------- | ------------------------------------------------- | ------------------------------- |\n| Avoid async void  | Prefer async Task methods over async void methods | Event handlers                  |\n| Async all the way | Don't mix blocking and async code                 | Console main method (C# \u003c= 7.0) |\n| Configure context | Use `ConfigureAwait(false)` when you can          | Methods that require con­text   |\n\n**异步方式处理**\n\n| To Do This ...                           | Instead of This ...        | Use This             |\n| ---------------------------------------- | -------------------------- | -------------------- |\n| Retrieve the result of a background task | `Task.Wait or Task.Result` | `await`              |\n| Wait for any task to complete            | `Task.WaitAny`             | `await Task.WhenAny` |\n| Retrieve the results of multiple tasks   | `Task.WaitAll`             | `await Task.WhenAll` |\n| Wait a period of time                    | `Thread.Sleep`             | `await Task.Delay`   |\n\n**最佳实践**\n\nasync/await 最适用于 IO 型任务（网络通信，数据库通信，http 请求等），但它不适用于计算型任务（遍历巨型列表，渲染处理图片等）。因为它会将保留线程释放到线程池，可用的 CPU/内核 将不涉及处理这些任务。因此，我们应该避免使用 Async/Await 进行计算型任务。\n\n对于处理计算型任务，倾向于结合 `TaskCreationOptions` 和 `LongRunning` 来使用 `Task.Factory.CreateNew`，它将启动一个新的后台线程来处理繁重的计算型任务，而不会将其释放回线程池，直到任务完成。\n\n**了解你的工具**\n\n关于 async 和 await 有太多地方需要学习， 有些困惑是很自然的。以下是一些常见问题的快速解决指南。\n\n**常见异步问题的解决方法**\n\n| Problem                                         | Solution                                                                          |\n| ----------------------------------------------- | --------------------------------------------------------------------------------- |\n| Create a task to execute code                   | `Task.Run` or `TaskFactory.StartNew` (not the `Task` constructor or `Task.Start`) |\n| Create a task wrapper for an operation or event | `TaskFactory.FromAsync` or `TaskCompletionSource\u003cT\u003e`                              |\n| Support cancellation                            | `CancellationTokenSource` and `CancellationToken`                                 |\n| Report progress                                 | `IProgress\u003cT\u003e` and `Progress\u003cT\u003e`                                                  |\n| Handle streams of data                          | TPL Dataflow or Reactive Extensions                                               |\n| Synchronize access to a shared resource         | `SemaphoreSlim`                                                                   |\n| Asynchronously initialize a resource            | `AsyncLazy\u003cT\u003e`                                                                    |\n| Async-ready producer/consumer structures        | TPL Dataflow or `AsyncCollection\u003cT\u003e`                                              |\n\n阅读 [Task-based Asynchronous Pattern (TAP) document](http://www.microsoft.com/download/en/details.aspx?id=19957)。它编写得非常好，包括有关 API 设计和正确使用异步/等待（包括取消和进度报告）的指导。\n\n应该使用新的 await-friendly 方法来替代旧的方法。如果你新的异步代码中有旧的示例，这说明你写错了。\n\n| Old                | New                                  | Description                                                   |\n| ------------------ | ------------------------------------ | ------------------------------------------------------------- |\n| `task.Wait`        | `await task`                         | Wait/await for a task to complete                             |\n| `task.Result`      | `await task`                         | Get the result of a completed task                            |\n| `Task.WaitAny`     | `await Task.WhenAny`                 | Wait/await for one of a collection of tasks to complete       |\n| `Task.WaitAll`     | `await Task.WhenAll`                 | Wait/await for every one of a collection of tasks to complete |\n| `Thread.Sleep`     | `await Task.Delay`                   | Wait/await for a period of time                               |\n| `Task` constructor | `Task.Run` or `TaskFactory.StartNew` | Create a code-based task                                      |\n\n\u003e Source https://gist.github.com/jonlabelle/841146854b23b305b50fa5542f84b20c\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n## 异常处理\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e异常处理的基本概念\u003c/b\u003e\u003c/summary\u003e\n\n抛出异常是一件好事！这意味了程序在运行时成功识别程序中出现的问题，通过停止当前堆栈上的函数执行，终止进程 (在 .NET/.NET Core 里)，并在控制台中通过堆栈跟踪来通知你。\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e在 catch 块中不要使用 'throw ex'\u003c/b\u003e\u003c/summary\u003e\n\n如果你在捕获一个异常之后需要重新抛出一个异常，仅仅使用 'throw' ，你将会保存堆栈跟踪，但在坏的情况下，你将丢失堆栈跟踪。\n\n**Bad:**\n\n```csharp\ntry\n{\n    // Do something..\n}\ncatch (Exception ex)\n{\n    // Any action something like roll-back or logging etc.\n    throw ex;\n}\n```\n\n**Good:**\n\n```csharp\ntry\n{\n    // Do something..\n}\ncatch (Exception ex)\n{\n    // Any action something like roll-back or logging etc.\n    throw;\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e不要忽略捕获的异常\u003c/b\u003e\u003c/summary\u003e\n\n对捕获到的错误置之不理并不能解决问题，抛出异常也好不了哪儿去，因为它们常常无法准确在控制台中显示出来。如果将一些代码放到 `try/catch` 中就意味着你认为这里可能会发生异常。因此你应该制定计划，创建代码分支，为异常发生做准备。\n\n**Bad:**\n\n```csharp\ntry\n{\n    FunctionThatMightThrow();\n}\ncatch (Exception ex)\n{\n    // silent exception\n}\n```\n\n**Good:**\n\n```csharp\ntry\n{\n    FunctionThatMightThrow();\n}\ncatch (Exception error)\n{\n    NotifyUserOfError(error);\n\n    // Another option\n    ReportErrorToService(error);\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用多个 catch 块而不是 if 条件\u003c/b\u003e\u003c/summary\u003e\n\n如果你需要对不同类型的异常做不同操作，你最好使用多个 catch 块来处理它们。\n\n**Bad:**\n\n```csharp\ntry\n{\n    // Do something..\n}\ncatch (Exception ex)\n{\n\n    if (ex is TaskCanceledException)\n    {\n        // Take action for TaskCanceledException\n    }\n    else if (ex is TaskSchedulerException)\n    {\n        // Take action for TaskSchedulerException\n    }\n}\n```\n\n**Good:**\n\n```csharp\ntry\n{\n    // Do something..\n}\ncatch (TaskCanceledException ex)\n{\n    // Take action for TaskCanceledException\n}\ncatch (TaskSchedulerException ex)\n{\n    // Take action for TaskSchedulerException\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e在重新引发异常时保留异常堆栈跟踪\u003c/b\u003e\u003c/summary\u003e\n\nC# 允许使用 \"throw\" 关键字在 catch 块中重新引发异常。使用 \"throw e;\" 抛出捕获的异常是一种不好的做法。此语句会重置堆栈跟踪。而使用 \"throw;\"，这将保持堆栈跟踪，并提供有关异常的更深入的信息。另一个选项是使用自定义异常。只需实例化新异常，并将其内部异常属性设置为捕获的异常，并引发 `new CustomException(\"some info\", e);` 。向异常添加信息是一种好的做法，因为它有助于调试。但是，如果目标是记录异常，则使用 \"throw;\" 将降级传递给调用方。\n\n**Bad:**\n\n```csharp\ntry\n{\n    FunctionThatMightThrow();\n}\ncatch (Exception ex)\n{\n    logger.LogInfo(ex);\n    throw ex;\n}\n```\n\n**Good:**\n\n```csharp\ntry\n{\n    FunctionThatMightThrow();\n}\ncatch (Exception error)\n{\n    logger.LogInfo(error);\n    throw;\n}\n```\n\n**Good:**\n\n```csharp\ntry\n{\n    FunctionThatMightThrow();\n}\ncatch (Exception error)\n{\n    logger.LogInfo(error);\n    throw new CustomException(error);\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n## 格式化\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e使用 \u003ci\u003e.editorconfig\u003c/i\u003e 文件\u003c/b\u003e\u003c/summary\u003e\n\n**Bad:**\n\n一个项目中有一些代码格式化文件，缩进样式在项目中被 `space` 和 `tab` 混淆。\n\n**Good:**\n\n在代码库中使用 `.editorconfig` 文件来定义和维护一致的代码样式。\n\n```csharp\nroot = true\n\n[*]\nindent_style = space\nindent_size = 2\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\n\n# C# files\n[*.cs]\nindent_size = 4\n# New line preferences\ncsharp_new_line_before_open_brace = all\ncsharp_new_line_before_else = true\ncsharp_new_line_before_catch = true\ncsharp_new_line_before_finally = true\ncsharp_new_line_before_members_in_object_initializers = true\ncsharp_new_line_before_members_in_anonymous_types = true\ncsharp_new_line_within_query_expression_clauses = true\n\n# Code files\n[*.{cs,csx,vb,vbx}]\nindent_size = 4\n\n# Indentation preferences\ncsharp_indent_block_contents = true\ncsharp_indent_braces = false\ncsharp_indent_case_contents = true\ncsharp_indent_switch_labels = true\ncsharp_indent_labels = one_less_than_current\n\n# avoid this. unless absolutely necessary\ndotnet_style_qualification_for_field = false:suggestion\ndotnet_style_qualification_for_property = false:suggestion\ndotnet_style_qualification_for_method = false:suggestion\ndotnet_style_qualification_for_event = false:suggestion\n\n# only use var when it's obvious what the variable type is\n# csharp_style_var_for_built_in_types = false:none\n# csharp_style_var_when_type_is_apparent = false:none\n# csharp_style_var_elsewhere = false:suggestion\n\n# use language keywords instead of BCL types\ndotnet_style_predefined_type_for_locals_parameters_members = true:suggestion\ndotnet_style_predefined_type_for_member_access = true:suggestion\n\n# name all constant fields using PascalCase\ndotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion\ndotnet_naming_rule.constant_fields_should_be_pascal_case.symbols  = constant_fields\ndotnet_naming_rule.constant_fields_should_be_pascal_case.style    = pascal_case_style\n\ndotnet_naming_symbols.constant_fields.applicable_kinds   = field\ndotnet_naming_symbols.constant_fields.required_modifiers = const\n\ndotnet_naming_style.pascal_case_style.capitalization = pascal_case\n\n# static fields should have s_ prefix\ndotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion\ndotnet_naming_rule.static_fields_should_have_prefix.symbols  = static_fields\ndotnet_naming_rule.static_fields_should_have_prefix.style    = static_prefix_style\n\ndotnet_naming_symbols.static_fields.applicable_kinds   = field\ndotnet_naming_symbols.static_fields.required_modifiers = static\n\ndotnet_naming_style.static_prefix_style.required_prefix = s_\ndotnet_naming_style.static_prefix_style.capitalization = camel_case\n\n# internal and private fields should be _camelCase\ndotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion\ndotnet_naming_rule.camel_case_for_private_internal_fields.symbols  = private_internal_fields\ndotnet_naming_rule.camel_case_for_private_internal_fields.style    = camel_case_underscore_style\n\ndotnet_naming_symbols.private_internal_fields.applicable_kinds = field\ndotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal\n\ndotnet_naming_style.camel_case_underscore_style.required_prefix = _\ndotnet_naming_style.camel_case_underscore_style.capitalization = camel_case\n\n# Code style defaults\ndotnet_sort_system_directives_first = true\ncsharp_preserve_single_line_blocks = true\ncsharp_preserve_single_line_statements = false\n\n# Expression-level preferences\ndotnet_style_object_initializer = true:suggestion\ndotnet_style_collection_initializer = true:suggestion\ndotnet_style_explicit_tuple_names = true:suggestion\ndotnet_style_coalesce_expression = true:suggestion\ndotnet_style_null_propagation = true:suggestion\n\n# Expression-bodied members\ncsharp_style_expression_bodied_methods = false:none\ncsharp_style_expression_bodied_constructors = false:none\ncsharp_style_expression_bodied_operators = false:none\ncsharp_style_expression_bodied_properties = true:none\ncsharp_style_expression_bodied_indexers = true:none\ncsharp_style_expression_bodied_accessors = true:none\n\n# Pattern matching\ncsharp_style_pattern_matching_over_is_with_cast_check = true:suggestion\ncsharp_style_pattern_matching_over_as_with_null_check = true:suggestion\ncsharp_style_inlined_variable_declaration = true:suggestion\n\n# Null checking preferences\ncsharp_style_throw_expression = true:suggestion\ncsharp_style_conditional_delegate_call = true:suggestion\n\n# Space preferences\ncsharp_space_after_cast = false\ncsharp_space_after_colon_in_inheritance_clause = true\ncsharp_space_after_comma = true\ncsharp_space_after_dot = false\ncsharp_space_after_keywords_in_control_flow_statements = true\ncsharp_space_after_semicolon_in_for_statement = true\ncsharp_space_around_binary_operators = before_and_after\ncsharp_space_around_declaration_statements = do_not_ignore\ncsharp_space_before_colon_in_inheritance_clause = true\ncsharp_space_before_comma = false\ncsharp_space_before_dot = false\ncsharp_space_before_open_square_brackets = false\ncsharp_space_before_semicolon_in_for_statement = false\ncsharp_space_between_empty_square_brackets = false\ncsharp_space_between_method_call_empty_parameter_list_parentheses = false\ncsharp_space_between_method_call_name_and_opening_parenthesis = false\ncsharp_space_between_method_call_parameter_list_parentheses = false\ncsharp_space_between_method_declaration_empty_parameter_list_parentheses = false\ncsharp_space_between_method_declaration_name_and_open_parenthesis = false\ncsharp_space_between_method_declaration_parameter_list_parentheses = false\ncsharp_space_between_parentheses = false\ncsharp_space_between_square_brackets = false\n\n[*.{asm,inc}]\nindent_size = 8\n\n# Xml project files\n[*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}]\nindent_size = 2\n\n# Xml config files\n[*.{props,targets,config,nuspec}]\nindent_size = 2\n\n[CMakeLists.txt]\nindent_size = 2\n\n[*.cmd]\nindent_size = 2\n\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n## 注释\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e避免位置标记\u003c/b\u003e\u003c/summary\u003e\n\n它们通常只会增加噪音。让函数和变量名称以及适当的缩进和格式为代码提供可视化结构。\n\n**Bad:**\n\n```csharp\n////////////////////////////////////////////////////////////////////////////////\n// Scope Model Instantiation\n////////////////////////////////////////////////////////////////////////////////\nvar model = new[]\n{\n    menu: 'foo',\n    nav: 'bar'\n};\n\n////////////////////////////////////////////////////////////////////////////////\n// Action setup\n////////////////////////////////////////////////////////////////////////////////\nvoid Actions()\n{\n    // ...\n};\n```\n\n**Bad:**\n\n```csharp\n\n#region Scope Model Instantiation\n\nvar model = {\n    menu: 'foo',\n    nav: 'bar'\n};\n\n#endregion\n\n#region Action setup\n\nvoid Actions() {\n    // ...\n};\n\n#endregion\n```\n\n**Good:**\n\n```csharp\nvar model = new[]\n{\n    menu: 'foo',\n    nav: 'bar'\n};\n\nvoid Actions()\n{\n    // ...\n};\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e不要在代码库中留下注释代码\u003c/b\u003e\u003c/summary\u003e\n\n版本控制存在是有原因的。只应该在历史记录中保留旧代码。\n\n**Bad:**\n\n```csharp\ndoStuff();\n// doOtherStuff();\n// doSomeMoreStuff();\n// doSoMuchStuff();\n```\n\n**Good:**\n\n```csharp\ndoStuff();\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e不要有日志注释\u003c/b\u003e\u003c/summary\u003e\n\n记住，使用版本控制！不需要废弃代码、注释代码，尤其是日志注释。使用 \"git log\" 获取历史记录！\n\n**Bad:**\n\n```csharp\n/**\n * 2018-12-20: Removed monads, didn't understand them (RM)\n * 2017-10-01: Improved using special monads (JP)\n * 2016-02-03: Removed type-checking (LI)\n * 2015-03-14: Added combine with type-checking (JR)\n */\npublic int Combine(int a,int b)\n{\n    return a + b;\n}\n```\n\n**Good:**\n\n```csharp\npublic int Combine(int a,int b)\n{\n    return a + b;\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cb\u003e只应该在业务逻辑较为复杂的时候才应该添加注释\u003c/b\u003e\u003c/summary\u003e\n\n注释是解释，不是要求，好的代码 _大部分_ 就是文档本身。\n\n**Bad:**\n\n```csharp\npublic int HashIt(string data)\n{\n    // The hash\n    var hash = 0;\n\n    // Length of string\n    var length = data.length;\n\n    // Loop through every character in data\n    for (var i = 0; i \u003c length; i++)\n    {\n        // Get character code.\n        const char = data.charCodeAt(i);\n        // Make the hash\n        hash = ((hash \u003c\u003c 5) - hash) + char;\n        // Convert to 32-bit integer\n        hash \u0026= hash;\n    }\n}\n```\n\n**Better but still Bad:**\n\n```csharp\npublic int HashIt(string data)\n{\n    var hash = 0;\n    var length = data.length;\n    for (var i = 0; i \u003c length; i++)\n    {\n        const char = data.charCodeAt(i);\n        hash = ((hash \u003c\u003c 5) - hash) + char;\n\n        // Convert to 32-bit integer\n        hash \u0026= hash;\n    }\n}\n```\n\n如果注释解释了代码正在执行的操作，它可能是一个无用的注释，可以使用一个命名良好的变量或函数来解决。前面的代码中的注释可以替换为名为 \"ConvertTo32bitInt\" 的函数，因此此注释仍然毫无用处。\n\n但是，开发人员选择 djb2 哈希算法而不是 sha-1 或其他哈希函数的代码就很难表达。在这种情况下，可以添加注释。\n\n**Good:**\n\n```csharp\npublic int Hash(string data)\n{\n    var hash = 0;\n    var length = data.length;\n\n    for (var i = 0; i \u003c length; i++)\n    {\n        var character = data[i];\n        // use of djb2 hash algorithm as it has a good compromise\n        // between speed and low collision with a very simple implementation\n        hash = ((hash \u003c\u003c 5) - hash) + character;\n\n        hash = ConvertTo32BitInt(hash);\n    }\n    return hash;\n}\n\nprivate int ConvertTo32BitInt(int value)\n{\n    return value \u0026 value;\n}\n```\n\n**[⬆ back to top](#目录)**\n\n\u003c/details\u003e\n\n# 其它关于代码整洁之道的资源\n\n## 其它代码整洁之道列表\n\n- [clean-code-javascript](https://github.com/ryanmcdermott/clean-code-javascript) - Clean Code concepts adapted for JavaScript\n- [clean-code-php](https://github.com/jupeter/clean-code-php) - Clean Code concepts adapted for PHP\n- [clean-code-ruby](https://github.com/uohzxela/clean-code-ruby) - Clean Code concepts adapted for Ruby\n- [clean-code-python](https://github.com/zedr/clean-code-python) - Clean Code concepts adapted for Python\n- [clean-code-typescript](https://github.com/labs42io/clean-code-typescript) - Clean Code concepts adapted for TypeScript\n- [clean-go-article](https://github.com/Pungyeon/clean-go-article) - Clean Code concepts adapted for Golang and an example how to apply [clean code in Golang](https://github.com/Pungyeon/clean-go)\n\n## 工具\n\n- [codemaid](https://github.com/codecadwallader/codemaid) - open source Visual Studio extension to cleanup and simplify our C#, C++, F#, VB, PHP, PowerShell, JSON, XAML, XML, ASP, HTML, CSS, LESS, SCSS, JavaScript and TypeScript coding\n- [Sharpen](https://github.com/sharpenrocks/Sharpen) - Visual Studio extension that intelligently introduces new C# features into your existing code base\n- [tslint-clean-code](https://github.com/Glavin001/tslint-clean-code) - TSLint rules for enforcing Clean Code\n\n## 表格\n\n- [Clean Code](cheetsheets/Clean-Code-V2.4.pdf) - The summary of [Clean Code: A Handbook of Agile Software Craftsmanship](https://www.amazon.com/dp/0132350882) book\n- [Clean Architecture](cheetsheets/Clean-Architecture-V1.0.pdf) - The summary of [Clean Architecture: A Craftsman's Guide to Software Structure and Design](https://www.amazon.com/dp/0134494164) book\n- [Modern JavaScript Cheatsheet](https://github.com/mbeaudru/modern-js-cheatsheet) - Cheatsheet for the JavaScript knowledge you will frequently encounter in modern projects\n- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org) - Cheatsheet was created to provide a concise collection of high value information on specific application security topics\n\n---\n\n# 贡献者\n\n感谢每位参与 `clean-code-dotnet` 项目贡献的朋友。\n\n\u003ca href=\"https://github.com/thangchung/clean-code-dotnet/graphs/contributors\"\u003e\u003cimg src=\"https://opencollective.com/cleancodedotnet/contributors.svg?width=890\" title=\"contributors\" alt=\"contributors\" /\u003e\u003c/a\u003e\n\n# 支持者\n\n热爱我们的工作，帮助我们继续我们的活动？[[成为支持者](https://opencollective.com/cleancodedotnet#backer)]\n\n\u003ca href=\"https://opencollective.com/cleancodedotnet#backers\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/cleancodedotnet/backers.svg?width=890\"\u003e\u003c/a\u003e\n\n# 赞助商\n\n成为赞助商，并在 Github 上的 README 上获取您的徽标，并链接到您的网站。[[成为赞助商](https://opencollective.com/cleancodedotnet#sponsor)]\n\n\u003ca href=\"https://opencollective.com/cleancodedotnet/sponsor/0/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/cleancodedotnet/sponsor/0/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/cleancodedotnet/sponsor/1/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/cleancodedotnet/sponsor/1/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/cleancodedotnet/sponsor/2/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/cleancodedotnet/sponsor/2/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/cleancodedotnet/sponsor/3/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/cleancodedotnet/sponsor/3/avatar.svg\"\u003e\u003c/a\u003e\n\u003ca href=\"https://opencollective.com/cleancodedotnet/sponsor/4/website\" target=\"_blank\"\u003e\u003cimg src=\"https://opencollective.com/cleancodedotnet/sponsor/4/avatar.svg\"\u003e\u003c/a\u003e\n\n# 许可证\n\n[![CC0](http://mirrors.creativecommons.org/presskit/buttons/88x31/svg/cc-zero.svg)](https://creativecommons.org/publicdomain/zero/1.0/)\n\nTo the extent possible under law, [thangchung](https://github.com/thangchung) has waived all copyright and related or neighboring rights to this work.\n\n","funding_links":["https://patreon.com/thangchung","https://opencollective.com/cleancodedotnet"],"categories":["General","C\\#","Design patterns","C#","C# #","一般","Clean Code","Uncategorized"],"sub_categories":["Resources","Uncategorized"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthangchung%2Fclean-code-dotnet","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fthangchung%2Fclean-code-dotnet","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fthangchung%2Fclean-code-dotnet/lists"}