diff --git a/.github/workflows/ConvertGuidelinesXmlToMarkdown.yml b/.github/workflows/ConvertGuidelinesXmlToMarkdown.yml new file mode 100644 index 00000000..14839ad3 --- /dev/null +++ b/.github/workflows/ConvertGuidelinesXmlToMarkdown.yml @@ -0,0 +1,53 @@ +#if the guidelines xml file is modified between commits to the master branch the associate markdown file is updated +name: Update csharp Markdown + +on: + push: + branches: + - 'master' + +jobs: + build: + runs-on: ubuntu-latest + + env: + XmlFileName: "Guidelines(8th Edition).xml" + + steps: + - uses: actions/checkout@v2 + with: + ref: ${{ github.ref }} + - name: Simple Diff + uses: mudlabs/simple-diff@v1.0.2 + id: diff + with: + # The path of the file or folder to find in the commits diff tree. + path: ./docs/${{ env.XmlFileName }} + # continue even if the xml file if the file isn't found in the diff tree (it wasn't changed). strict=true causes the action to fail otherwise + strict : false + - name: Guidelines xml status + run: | + echo "${{ env.XmlFileName }} modified = ${{ steps.diff.outputs.modified }}" + echo "${{ steps.diff.outputs.name }}" + + - name: Setup .NET Core + uses: actions/setup-dotnet@v1 + with: + dotnet-version: 3.1.301 + if: steps.diff.outputs.modified == true + + - name: restore_compile_run_createMD + run: | + dotnet restore + dotnet run --configuration Release --project ./XMLtoMD/GuidelineXmlToMD/GuidelineXmlToMD.csproj + ./XMLtoMD/GuidelineXmlToMD/bin/Release/netcoreapp3.1/GuidelineXmlToMD "${{ env.XmlFileName }}" + if: steps.diff.outputs.modified == true + + - name: Create commit and push to CodingGuideLinesMDUpdate + run: | + git config user.name '${{ github.actor }}' + git config user.email '${{ github.actor }}@users.noreply.github.com' + git add -A + git commit -m "new coding guidelines MD File created" + git push origin '${{ github.ref }}' + if: steps.diff.outputs.modified == true diff --git a/XMLtoMD/GuidelineXmlToMD/GlobalSuppressions.cs b/XMLtoMD/GuidelineXmlToMD/GlobalSuppressions.cs new file mode 100644 index 00000000..f66511f5 --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/GlobalSuppressions.cs @@ -0,0 +1,11 @@ +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. +// Project-level suppressions either have no target or are given +// a specific target and scoped to a namespace, type, member, etc. + +using System.Diagnostics.CodeAnalysis; + +[assembly: SuppressMessage("Globalization", "CA1307:Specify StringComparison", Justification = "")] +[assembly: SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "From an Old Github Project, not for production")] +[assembly: SuppressMessage("Design", "CA1062:Validate arguments of public methods", Justification = "")] + diff --git a/XMLtoMD/GuidelineXmlToMD/Guideline.cs b/XMLtoMD/GuidelineXmlToMD/Guideline.cs new file mode 100644 index 00000000..4dc2ad37 --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/Guideline.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; + + +namespace GuidelineXmlToMD +{ + public class Guideline + { + public string Key { get; set; } = ""; + public string Text { get; set; } = ""; + + public string Severity { get; set; } = ""; + + public string Section { get; set; } = ""; + public string Subsection { get; set; } = ""; + + public List Comments { get;} = new List(); + + public override int GetHashCode() + { + return Key.GetHashCode(); + } + + public override bool Equals(object obj) + { + Guideline otherGuideline = obj as Guideline; + + return otherGuideline != null && string.Equals(otherGuideline.Key, this.Key); + } + + } + +} diff --git a/XMLtoMD/GuidelineXmlToMD/GuidelineXmlFileReader.cs b/XMLtoMD/GuidelineXmlToMD/GuidelineXmlFileReader.cs new file mode 100644 index 00000000..a92e6bea --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/GuidelineXmlFileReader.cs @@ -0,0 +1,42 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Xml.Linq; + +namespace GuidelineXmlToMD +{ + static class GuidelineXmlFileReader + { + public const string _Guideline = "guideline"; + public const string _Key = "key"; + public const string _Severity = "severity"; + + public const string _Section = "section"; + public const string _Subsection = "subsection"; + public const string _Comments = "comments"; + + + public static ICollection ReadExisitingGuidelinesFile(string pathToExistingGuidelinesXml) + { + + XDocument previousGuidelines = XDocument.Load(pathToExistingGuidelinesXml); + + HashSet guidelines = new HashSet(); + + foreach (XElement guidelineFromXml in previousGuidelines.Root.DescendantNodes().OfType()) + { + Guideline guideline = new Guideline(); + guideline.Severity = guidelineFromXml.Attribute(_Severity)?.Value; + guideline.Subsection = guidelineFromXml.Attribute(_Subsection)?.Value; + guideline.Section = guidelineFromXml.Attribute(_Section)?.Value; + guideline.Text = guidelineFromXml?.Value; + guideline.Key = guidelineFromXml.Attribute(_Key)?.Value; + + guidelines.Add(guideline); + } + return guidelines; + } + + } +} diff --git a/XMLtoMD/GuidelineXmlToMD/GuidelineXmlToMD.csproj b/XMLtoMD/GuidelineXmlToMD/GuidelineXmlToMD.csproj new file mode 100644 index 00000000..d77effe4 --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/GuidelineXmlToMD.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp3.1 + + + + + + + + + + + diff --git a/XMLtoMD/GuidelineXmlToMD/GuidelineXmlToMD.sln b/XMLtoMD/GuidelineXmlToMD/GuidelineXmlToMD.sln new file mode 100644 index 00000000..4983429a --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/GuidelineXmlToMD.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30225.117 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "GuidelineXmlToMD", "GuidelineXmlToMD.csproj", "{D9C7CC15-01DB-46FE-922A-6EB41CE1759B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D9C7CC15-01DB-46FE-922A-6EB41CE1759B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9C7CC15-01DB-46FE-922A-6EB41CE1759B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9C7CC15-01DB-46FE-922A-6EB41CE1759B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9C7CC15-01DB-46FE-922A-6EB41CE1759B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {A1FCF330-0100-466C-A566-FF92F0B59E10} + EndGlobalSection +EndGlobal diff --git a/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdExtensions.cs b/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdExtensions.cs new file mode 100644 index 00000000..959d4aa4 --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdExtensions.cs @@ -0,0 +1,47 @@ +namespace MarkdownOut { + + /// + /// A container for extension methods related to Markdown styling and formatting. + /// + public static class MdExtensions { + + /// + /// Styles one or more substrings of the provided string using the specified + /// . + /// + /// The string containing the substring to style. + /// The substring to style. + /// The Markdown style to apply. + /// + /// If true, only the first occurrence of the substring is styled; otherwise, all + /// occurrences of the substring are styled. + /// + /// + /// The selectively styled string. If does not contain any + /// occurrences of , it is returned unchanged. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Globalization", "CA1303:Do not pass literals as localized parameters", Justification = "")] + public static string StyleSubstring(this string str, string substring, MdStyle style, + bool firstOnly = false) { + if (string.IsNullOrEmpty(str)) + { + throw new System.ArgumentException("cannot stylize empty string", nameof(str)); + } + + if (string.IsNullOrEmpty(substring)) + { + throw new System.ArgumentException("cannot stylize empty string", nameof(str)); + } + + if (!firstOnly) { + return str?.Replace(substring, MdText.Style(substring, style), System.StringComparison.Ordinal); + } + int pos = str.IndexOf(substring, System.StringComparison.Ordinal); + if (pos < 0) { + return str; + } + return str.Substring(0, pos) + MdText.Style(substring, style) + + str.Substring(pos + substring.Length); + } + } +} diff --git a/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdFormat.cs b/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdFormat.cs new file mode 100644 index 00000000..fbca6269 --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdFormat.cs @@ -0,0 +1,65 @@ +namespace MarkdownOut { + + /// + /// Specifies a Markdown format to apply to a line or block of text. Formats are applied with a + /// prefix string and, unlike Markdown styles (see ), cannot be selectively + /// applied to substrings. + /// + public enum MdFormat { + + /// + /// No text format. + /// + None, + + /// + /// Heading 1 text format (inserts the string "# " in front of text). + /// + Heading1, + + /// + /// Heading 2 text format (inserts the string "## " in front of text). + /// + Heading2, + + /// + /// Heading 3 text format (inserts the string "### " in front of text). + /// + Heading3, + + /// + /// Heading 4 text format (inserts the string "#### " in front of text). + /// + Heading4, + + /// + /// Heading 5 text format (inserts the string "##### " in front of text). + /// + Heading5, + + /// + /// Heading 6 text format (inserts the string "###### " in front of text). + /// + Heading6, + + /// + /// Quote text format (inserts the string "> " in front of text). + /// + Quote, + + /// + /// Unordered list item text format (inserts the string "- " in front of text). + /// + UnorderedListItem, + + /// + /// Ordered list item text format (inserts the string "1. " in front of text). + /// + OrderedListItem, + + /// + /// Creates a link to the heading that matches text to write (formats the text as [Foo](#foo)). + /// + InternalLink, + } +} diff --git a/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdStyle.cs b/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdStyle.cs new file mode 100644 index 00000000..b4ebe64f --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdStyle.cs @@ -0,0 +1,40 @@ +namespace MarkdownOut { + + /// + /// Specifies a Markdown style to apply to a string of text. Styles are applied by wrapping text + /// with a special string on each side and can be selectively applied to substrings. + /// + public enum MdStyle { + + /// + /// No text styling. + /// + None, + + /// + /// Italic text styling (surrounds text with a single * character on each side). + /// + Italic, + + /// + /// Bold text styling (surrounds text with two * characters on each side). + /// + Bold, + + /// + /// Bold italic text styling (surrounds text with three * characters on each side). + /// + BoldItalic, + + /// + /// Code text styling (surrounds text with a single ` character on each side). + /// + Code, + + /// + /// Strike-through text styling (surrounds text with two ~ characters on each side). + /// This style may not be supported by all Markdown parsers. + /// + StrikeThrough, + } +} \ No newline at end of file diff --git a/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdText.cs b/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdText.cs new file mode 100644 index 00000000..78d5b1e0 --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdText.cs @@ -0,0 +1,225 @@ +using System; +using System.Text.RegularExpressions; + +namespace MarkdownOut { + + /// + /// Provides static fields and methods to style and format Markdown text. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Naming", "INTL0001:Fields _PascalCase", Justification = "From an Old Github Project, not for production")] + public static class MdText { + + /// + /// The Markdown tab string (four spaces). + /// + public static readonly string Tab = new string(' ', 4); + + + /// + /// The Markdown line break string (two spaces followed by a newline). + /// + + public static readonly string LineBreak = new string(' ', 2) + "\r\n"; + + /// + /// The Markdown paragraph break string (two newlines). + /// + public static readonly string ParagraphBreak = "\r\n\r\n"; + /// + /// The Markdown paragraph break string (two newlines). + /// + public static readonly string ParagraphBreakOneLine = "\r\n"; + + /// + /// The Markdown italic string to be wrapped around a string of text. + /// + public static readonly string ItalicWrap = "*"; + + /// + /// The Markdown bold string to be wrapped around a string of text. + /// + public static readonly string BoldWrap = "**"; + + /// + /// The Markdown code string to be wrapped around a string of text. + /// + public static readonly string CodeWrap = "`"; + + /// + /// The Markdown strike-through string to be wrapped around a string of text. + /// + public static readonly string StrikeThroughWrap = "~~"; + + /// + /// The Markdown heading 1 prefix string. + /// + public static readonly string Heading1Prefix = "# "; + + /// + /// The Markdown heading 2 prefix string. + /// + public static readonly string Heading2Prefix = "## "; + + /// + /// The Markdown heading 3 prefix string. + /// + public static readonly string Heading3Prefix = "### "; + + /// + /// The Markdown heading 4 prefix string. + /// + public static readonly string Heading4Prefix = "#### "; + + /// + /// The Markdown heading 5 prefix string. + /// + public static readonly string Heading5Prefix = "##### "; + + /// + /// The Markdown heading 6 prefix string. + /// + public static readonly string Heading6Prefix = "###### "; + + /// + /// The Markdown quote prefix string. + /// + public static readonly string QuotePrefix = "> "; + + /// + /// The Markdown unordered list item prefix string. + /// + public static readonly string UnorderedListItemPrefix = "- "; + + + /// + /// The default number used for ordered list items. + /// + public static readonly int DefaultListItemNumber = 1; + + /// + /// The Markdown ordered list item prefix string. + /// + public static readonly string OrderedListItemPrefix = DefaultListItemNumber + ". "; + + /// + /// The Markdown indent string for sublists. + /// + public static readonly string ListItemIndent = new string(' ', 5); + + /// + /// Styles the provided text using the specified . + /// + /// The text to style. + /// The Markdown style to apply. + /// The styled string. + /// The style is not recognized. + public static string Style(object text, MdStyle style) { + string wrap; + switch (style) { + case MdStyle.None: wrap = string.Empty; break; + case MdStyle.Italic: wrap = ItalicWrap; break; + case MdStyle.Bold: wrap = BoldWrap; break; + case MdStyle.BoldItalic: wrap = ItalicWrap + BoldWrap; break; + case MdStyle.Code: wrap = CodeWrap; break; + case MdStyle.StrikeThrough: wrap = StrikeThroughWrap; break; + default: throw new ArgumentException("The style is not recognized."); + } + return wrap + text + wrap; + } + + /// + /// Formats the provided text using the specified . If styling is also + /// desired, use the method or style the text first; formatting + /// text and then styling it produces output that may not parse correctly. + /// + /// The text to format. + /// The Markdown format to apply. + /// The formatted string. + /// The format is not recognized. + public static string Format(object text, MdFormat format) { + string prefix; + switch (format) { + case MdFormat.None: prefix = string.Empty; break; + case MdFormat.Heading1: prefix = Heading1Prefix; break; + case MdFormat.Heading2: prefix = Heading2Prefix; break; + case MdFormat.Heading3: prefix = Heading3Prefix; break; + case MdFormat.Heading4: prefix = Heading4Prefix; break; + case MdFormat.Heading5: prefix = Heading5Prefix; break; + case MdFormat.Heading6: prefix = Heading6Prefix; break; + case MdFormat.Quote: prefix = QuotePrefix; break; + case MdFormat.UnorderedListItem: prefix = UnorderedListItemPrefix; break; + case MdFormat.OrderedListItem: prefix = OrderedListItemPrefix; break; +#pragma warning disable CA1308 // Normalize strings to uppercase + case MdFormat.InternalLink: prefix = ""; text = $"[{text}](#{RemoveInvalidMDLinkCharacters(text).ToLowerInvariant()})"; break; +#pragma warning restore CA1308 // Normalize strings to uppercase + default: throw new ArgumentException("The format is not recognized."); + } + return prefix + text; + } + + public static string RemoveInvalidMDLinkCharacters(object text) + { + return Regex.Replace(text.ToString(), "[^A-Za-z]", ""); + } + + /// + /// Styles then formats the provided text using the specified and + /// . + /// + /// The text to format and style. + /// The Markdown style to apply. + /// The Markdown format to apply. + /// The formatted and styled string. + public static string StyleAndFormat(object text, MdStyle style, MdFormat format) { + return Format(Style(text, style), format); + } + + /// + /// Applies an indent to the provided text. + /// + /// The text to indent. + /// + /// The indent length to apply (0 adds no indent, 1 adds a single indentation to create a + /// sublist, etc). Negative values are ignored. + /// + /// The indented string. + public static string Indent(object text, int indent) { + string indentPrefix = String.Empty; + while (indent > 0) { + indentPrefix += ListItemIndent; + indent--; + } + return indentPrefix + text; + } + + /// + /// Cleanses the provided text by replacing all tab characters with the + /// string and ensuring that all newlines are the Windows \r\n. + /// + /// The text to cleanse. + /// + /// If true, all newlines will be replaced by Markdown line breaks instead of just Windows + /// newlines. This is useful when cleansing text formatted as a list item as it maintains + /// the desired line breaks in the list item's text. + /// + /// The cleansed text with consistent newlines and no tab characters. + public static string Cleanse(string text, bool useMdLineBreaks = false) { + //first, replace all tab characters with the Tab string + text = text.Replace("\t", Tab); + /* + * Next, replace all Windows newlines with the Unix newline; this is done to make sure + * the following code doesn't replace instances of "\r\n" with "\r\n\n". All of this is + * done to make sure the raw text displays correctly on Windows machines (programs such + * as Visual Studio or Notepad do not recognize "\n" as a newline, just "\r\n"). + */ + text = text.Replace("\r\n", "\n").Replace("\r", "\n"); + //finally, replace all Unix newlines with Windows newlines or Markdown line breaks + if (useMdLineBreaks) { + return text.Replace("\n", LineBreak); + } + else { + return text.Replace("\n", "\r\n"); + } + } + } +} diff --git a/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdWriter.cs b/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdWriter.cs new file mode 100644 index 00000000..1da6f685 --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/MarkdownOut/MdWriter.cs @@ -0,0 +1,177 @@ +using System; +using System.IO; + +namespace MarkdownOut // by https://github.com/rob-williams/MarkdownOut +{ + + /// + /// A wrapper around used to write Markdown text to a file. + /// + public class MdWriter : IDisposable { + + private StreamWriter _Stream; + private bool _IsDisposed; + + /// + /// Initializes a new instance of the class, opening a stream to the + /// file at the specified path. + /// + /// The full path to the output file, including file extension. + /// + /// If true, output is appended to the file's existing contents; otherwise, the file is + /// overwritten. + /// + public MdWriter(string path, bool append = false) { + _Stream = new StreamWriter(path, append); + } + + #region IDisposable + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting + /// unmanaged resources. + /// + // Dispose() calls Dispose(true) + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + // The bulk of the clean-up code is implemented in Dispose(bool) + protected virtual void Dispose(bool disposing) + { + if (_IsDisposed) return; + + if (disposing) + { + // free managed resources + _Stream.Dispose(); + } + + // free native resources if there are any. + + _IsDisposed = true; + } + + + #endregion + + + /// + /// Writes the provided output to the file. + /// + /// The output to write. + /// The optional Markdown style to apply. + /// The optional Markdown format to apply. + /// + /// If true, when the text is cleansed, all newlines will be replaced by Markdown line + /// breaks to maintain the assumed intended line breaks in the text; otherwise, the text's + /// newlines will not parsed as line breaks by Markdown parsers. + /// + public void Write(object output, MdStyle style = MdStyle.None, + MdFormat format = MdFormat.None, bool useMdLineBreaks = true) { + string text = MdText.StyleAndFormat(output, style, format); + _Stream.Write(MdText.Cleanse(text, useMdLineBreaks)); + } + + /// + /// Writes the provided output to the file, followed by a Markdown paragraph break to + /// terminate the line and start a new paragraph. + /// + /// The output to write. + /// The optional Markdown style to apply. + /// The optional Markdown format to apply. + /// + /// If true, when the text is cleansed, all newlines will be replaced by Markdown line + /// breaks to maintain the assumed intended line breaks in the text; otherwise, the text's + /// newlines will not parsed as line breaks by Markdown parsers. + /// + public void WriteLine(object output, MdStyle style = MdStyle.None, + MdFormat format = MdFormat.None, bool useMdLineBreaks = true, int numNewLines = 2) { + string text = MdText.StyleAndFormat(output, style, format); + _Stream.Write(MdText.Cleanse(text, useMdLineBreaks) + MakeParagraphLineBreak(numNewLines)); + } + + /// + /// Writes the provided output to the file, followed by a Markdown line break to terminate + /// the line but not start a new paragraph. + /// + /// The output to write. + /// The optional Markdown style to apply. + /// + /// The optional Markdown format to apply. Some formats may not be terminated by the + /// Markdown line break. + /// + /// + /// If true, when the text is cleansed, all newlines will be replaced by Markdown line + /// breaks to maintain the assumed intended line breaks in the text; otherwise, the text's + /// newlines will not parsed as line breaks by Markdown parsers. + /// + public void WriteLineSingle(object output, MdStyle style = MdStyle.None, + MdFormat format = MdFormat.None, bool useMdLineBreaks = true) { + string text = MdText.StyleAndFormat(output, style, format); + _Stream.Write(MdText.Cleanse(text, useMdLineBreaks) + MdText.LineBreak); + } + + /// + /// Writes the provided output to the file using the + /// format, followed by a Markdown paragraph break + /// to terminate the list item. + /// + /// The output to write. + /// + /// The optional indent of the list item (0 adds no indent, 1 adds a single indentation to + /// create a sublist, etc). Negative values are ignored. + /// + /// The optional Markdown style to apply. + + /// The optional Markdown format to apply. + /// + public void WriteUnorderedListItem(object output, int listIndent = 0, + MdStyle style = MdStyle.None, MdFormat format = MdFormat.None, int numNewLines=1) { + string text = MdText.Format(output, format); + text = MdText.StyleAndFormat(text, style, MdFormat.UnorderedListItem); + text = MdText.Indent(text, listIndent); + _Stream.Write(MdText.Cleanse(text, true) + + MakeParagraphLineBreak(numNewLines)); + } + + private static string MakeParagraphLineBreak(int numNewLines) + { + string breaks = ""; + for (int i = 0; i < numNewLines; i++) + { + breaks = breaks + MdText.ParagraphBreakOneLine; + } + return breaks; + } + + + + /// + /// Writes the provided output to the file using the + /// format, followed by a Markdown paragraph break to terminate the list item. + /// + /// The output to write. + /// + /// The optional item number of the list item. Does not affect parsed Markdown output or the + /// list order, only the raw text. Negative values are ignored. + /// + /// + /// The optional indent of the list item (0 adds no indent, 1 adds a single indentation to + /// create a sublist, etc). Negative values are ignored. + /// + /// The optional Markdown style to apply. + public void WriteOrderedListItem(object output, int itemNumber = 1, int listIndent = 0, + MdStyle style = MdStyle.None) { + string text = MdText.StyleAndFormat(output, style, MdFormat.OrderedListItem); + //replace the list item number supplied by MdText with the number provided + if (itemNumber != MdText.DefaultListItemNumber && itemNumber >= 0) { + text = itemNumber + text.Substring(1); + } + text = MdText.Indent(text, listIndent); + _Stream.Write(MdText.Cleanse(text, true) + MdText.ParagraphBreak); + } + } +} diff --git a/XMLtoMD/GuidelineXmlToMD/Program.cs b/XMLtoMD/GuidelineXmlToMD/Program.cs new file mode 100644 index 00000000..2bb333f2 --- /dev/null +++ b/XMLtoMD/GuidelineXmlToMD/Program.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Text.RegularExpressions; +using MarkdownOut; + + +namespace GuidelineXmlToMD +{ + class Program + { + static MarkdownOut.MdWriter _MdWriter; + static void Main(string[] args) + { + string xmlFileName = "Guidelines(8th Edition).xml"; + if (args.Length != 0) { //check for input fileName + if (Regex.Match(args[0], @".*.xml").Success) { + xmlFileName = args[0]; + } + } + + Match repoRefFolder = Regex.Match(AssemblyDirectory, @$".*CodingGuidelines"); + + string[] xmlFilePath = { repoRefFolder.Value, "docs", xmlFileName}; + ICollection guidelines = GuidelineXmlFileReader.ReadExisitingGuidelinesFile(Path.Combine(xmlFilePath)); + + + string mdFileName = "csharp.md"; + string[] mdFilePath = { repoRefFolder.Value, "docs", "coding", mdFileName}; + + using (_MdWriter = new MdWriter(Path.Combine(mdFilePath))) + { + + + _MdWriter.WriteLine("C# Guidelines", format: MdFormat.Heading1); + PrintSections(guidelines); + _MdWriter.WriteLine("Guidelines", format: MdFormat.Heading1); + _MdWriter.WriteLine(""); + PrintGuidelinesBySection(guidelines); + } + + + + + //C: \Users\saffron\source\repos\CodingGuidelines\XMLtoMD\GuidelineXmlToMD\bin\Debug\netcoreapp3.1 + //C: \Users\saffron\source\repos\CodingGuidelines\docs + + + } + + private static void PrintGuidelinesBySection(ICollection guidelines) + { + foreach (string section in GetSections(guidelines)) + { + _MdWriter.WriteLine(""); + Console.WriteLine(section); + _MdWriter.WriteLine(section, format: MdFormat.Heading2,style: MdStyle.BoldItalic); + + + + IEnumerable guidelinesInSections = (from guideline in guidelines + where string.Equals(guideline.Section, section, StringComparison.OrdinalIgnoreCase) + select guideline); + + foreach (string subsection in GetSubSections(guidelines)) + { + + IEnumerable guidelinesInSubsection = (from guideline in guidelinesInSections + where string.Equals(guideline.Subsection, subsection, StringComparison.OrdinalIgnoreCase) + select guideline); + + if (guidelinesInSubsection.Any()) + { + Console.WriteLine($" { subsection}"); + _MdWriter.WriteLine(subsection, format: MdFormat.Heading3, numNewLines: 1); + + foreach (Guideline guideline in guidelinesInSubsection) + { + _MdWriter.WriteUnorderedListItem(GetGuidelineEmoji(guideline) + " " + guideline.Text.Trim('"'), listIndent: 0); + } + } + _MdWriter.WriteLine("", numNewLines: 1); + + } + } + } + + private static string GetGuidelineEmoji(Guideline guideline) + { + string emoji = ""; + switch (guideline.Severity) + { + case "AVOID": + emoji = ":no_entry:"; + break; + case "DO NOT": + emoji = ":x:"; + break; + case "DO": + emoji = ":heavy_check_mark:"; + break; + case "CONSIDER": + emoji = ":grey_question:"; + break; + + default: + break; + } + return emoji; + } + + private static void PrintSections(ICollection guidelines) + { + _MdWriter.WriteLine("Sections", format: MdFormat.Heading2); + + List subSections = new List(); + + List sections = GetSections(guidelines); + foreach (string section in sections) + { + Console.WriteLine(section); + + _MdWriter.WriteUnorderedListItem(section, format: MdFormat.InternalLink, listIndent: 0); + + + subSections = (from guideline in guidelines + where string.Equals(guideline.Section, section, System.StringComparison.OrdinalIgnoreCase) + select guideline.Subsection).Distinct().OrderBy(x => x).ToList(); + + foreach (string subsection in subSections) + { + Console.WriteLine($" { subsection}"); + _MdWriter.WriteUnorderedListItem(subsection, format: MdFormat.InternalLink, listIndent: 1); + } + _MdWriter.WriteLine("", numNewLines: 1); + + + + } + + + + } + + public static List GetSections(ICollection guidelines) + { + + List sections = (from guideline in guidelines + select guideline.Section).Distinct().OrderBy(x => x).ToList(); + return sections; + } + + public static List GetSubSections(ICollection guidelines) + { + + List subSections = (from guideline in guidelines + select guideline.Subsection).Distinct().OrderBy(x => x).ToList(); + return subSections; + } + + public static string AssemblyDirectory + { + get + { + string codeBase = Assembly.GetExecutingAssembly().CodeBase; + UriBuilder uri = new UriBuilder(codeBase); + string path = Uri.UnescapeDataString(uri.Path); + return Path.GetDirectoryName(path); + } + } + } + + + + + +} diff --git a/docs/coding/csharp.md b/docs/coding/csharp.md index a8005663..a582995d 100644 --- a/docs/coding/csharp.md +++ b/docs/coding/csharp.md @@ -1,363 +1,435 @@ -# C# Coding Standards +# C# Guidelines ## Sections -- [Naming](#naming) - - [Abbreviations](#abbreviations) - - [Casing](#casing) - - [Namespaces](#namespaces) - - [Types](#types) - - [Methods](#methods) - - [Variables and Fields](#variables-and-fields) - - [Miscellaneous](#miscellaneous) - -- [Layout](#layout) - - [Comments](#comments) - - [Files](#files) - - [Whitespace](#whitespace) - [Coding](#coding) - - [Arrays](#arrays) - - [Assemblies](#assemblies) - - [Branches](#branches) - - [Classes](#classes) - - [Conversions](#conversions) - - [Dispose()](#dispose) - - [Enums](#enums) - - [Equality](#equality) - - [Exceptions](#exceptions) - - [Fields](#fields) - - [Flags](#flags) - - [Increment/decrement](#incrementdecrement) - - [Interfaces](#interfaces) - - [Methods](#methods-1) - - [Parameters](#parameters) - - [Properties](#properties) - - [Strings](#strings) - - [Structs](#structs) - - [Synchronization](#synchronization) - - [Tasks](#tasks) - - [Threads](#threads) - - [ToString()](#tostring) - - [Types](#types) - - [Miscellaneous](#miscellaneous) - - - -## Naming + - [Arrays](#arrays) + - [Assemblies](#assemblies) + - [Branches](#branches) + - [Classes](#classes) + - [Conversions](#conversions) + - [Dispose()](#dispose) + - [Enums](#enums) + - [Equality](#equality) + - [Exceptions](#exceptions) + - [Fields](#fields) + - [Flags](#flags) + - [Increment/decrement](#incrementdecrement) + - [Interfaces](#interfaces) + - [Methods](#methods) + - [Miscellaneous](#miscellaneous) + - [Parameters](#parameters) + - [Properties](#properties) + - [Strings](#strings) + - [Structs](#structs) + - [Synchronization](#synchronization) + - [Tasks](#tasks) + - [Threads](#threads) + - [ToString()](#tostring) + - [Types](#types) -### Abbreviations -- :x: DO NOT: use abbreviations or contractions within identifier names. +- [Layout](#layout) + - [Comments](#comments) + - [Files](#files) + - [Whitespace](#whitespace) -### Casing -- :heavy_check_mark: DO: capitalize both characters in two-character acronyms, except for the first word of a camelCased identifier. -- :heavy_check_mark: DO: capitalize only the first character in acronyms with three or more characters, except for the first word of a camelCased identifier. -- :x: DO NOT: capitalize any of the characters in acronyms at the beginning of a camelCased identifier. -- :heavy_check_mark: DO: use PascalCasing for all class names. -- :heavy_check_mark: DO: use camelCasing for local variable names. -- :heavy_check_mark: DO: use uppercase literal suffixes (e.g., 1.618033988749895M). -- :heavy_check_mark: DO: use camelCasing for variable declarations using tuple syntax. -- :grey_question: CONSIDER: using PascalCasing for all tuple item names. -- :heavy_check_mark: DO: use PascalCasing for namespace names. -- :heavy_check_mark: DO: use camelCasing for parameter names. -- :grey_question: CONSIDER: using the same casing on a property's backing field as that used in the property, distinguishing the backing field with an "_" prefix. Do not, however, use two underscores; identifiers beginning with two underscores are reserved for the use of t. -- :no_entry: AVOID: naming fields with camelCase. -- :heavy_check_mark: DO: name properties with PascalCase. -- :heavy_check_mark: DO: use PascalCasing and an "I" prefix for interface names. +- [Naming](#naming) + - [Abbreviations](#abbreviations) + - [Casing](#casing) + - [Methods](#methods) + - [Miscellaneous](#miscellaneous) + - [Namespaces](#namespaces) + - [Types](#types) + - [Variables and fields](#variablesandfields) -### Namespaces -- :heavy_check_mark: DO: prefix namespace names with a company name to prevent namespaces from different companies having the same name. -- :heavy_check_mark: DO: use a stable, version-independent product name at the second level of a namespace name. +# Guidelines -### Types -- :x: DO NOT: define types without placing them into a namespace. -- :heavy_check_mark: DO: choose meaningful names for type parameters and prefix the name with T. -- :grey_question: CONSIDER: indicating a constraint in the name of a type parameter. -- :no_entry: AVOID: shadowing a type parameter of an outer type with an indentically named type parameter. -### Methods -- :heavy_check_mark: DO: give methods names that are verbs or verb phrases. -### Variables and fields -- :heavy_check_mark: DO: favor clarity over brevity when naming identifiers. -- :x: DO NOT: use Hungarian notation (that is, do not encode the type of a variable in its name). -- :heavy_check_mark: DO: name classes with nouns or noun phrases. -- :heavy_check_mark: DO: use the C# keyword rather than the BCL name when specifying a data type (e.g., string rather than String). -- :x: DO NOT: use a constant for any value that can possibly change over time. The value of pi and the number of protons in an atom of gold are constants; the price of gold, the name of your company, and the version number of your program can change. -- :heavy_check_mark: DO: name properties using a noun, noun phrase, or adjective. -- :heavy_check_mark: DO: favor prefixing Boolean properties with "Is," "Can," or "Has," when that practice adds value. -- :heavy_check_mark: DO: declare all instance fields as private (and expose them via a property). -- :heavy_check_mark: DO: use constant fields for values that will never change. -- :no_entry: AVOID: constant fields for values that will change over time. -- :x: DO NOT: include “sentinel” values (such as a value called Maximum); such values can be confusing to the user. -### Miscellaneous -- :heavy_check_mark: DO: treat parameter names as part of the API, and avoid changing the names if version compatibility between APIs is important. -- :heavy_check_mark: DO: name the source file with the name of the public type it contains. -- :grey_question: CONSIDER: giving a property the same name as its type. -- :heavy_check_mark: DO: use the same name for constructor parameters (camelCase) and properties (PascalCase) if the constructor parameters are used to simply set the property. -- :heavy_check_mark: DO: name an exception classes with the "Exception" suffix. -## Layout +## ***Coding*** -### Files -- :grey_question: CONSIDER: organizing the directory hierarchy for source code files to match the namespace hierarchy. -- :x: DO NOT: place more than one class in a single source file. -- :grey_question: CONSIDER: creating a folder structure that matches the namespace hierarchy. - -### Comments -- :x: DO NOT: use comments unless they describe something that is not obvious to someone other than the developer who wrote the code. -- :heavy_check_mark: DO: favor writing clearer code over entering comments to clarify a complicated algorithm. -- :heavy_check_mark: DO: provide XML comments on public APIs when they provide more context than the API signature alone. This includes member descriptions, parameter descriptions, and examples of calling the API. - -### Whitespace -- :heavy_check_mark: DO: use parentheses to make code more readable, particularly if the operator precedence is not clear to the casual reader. -- :no_entry: AVOID: omitting braces, except for the simplest of single-line if statements. - -## Coding ### Arrays -- :grey_question: CONSIDER: checking for null before accessing an array rather than assuming there is an array instance. -- :grey_question: CONSIDER: checking the array length before indexing into an array rather than assuming the length. -- :grey_question: CONSIDER: using the index from end operator (^) rather than Length - 1 with C# 8.0 or higher. -- :heavy_check_mark: DO: use parameter arrays when a method can handle any number—including zero—of additional arguments. -- :no_entry: AVOID: unsafe array covariance. Instead, CONSIDER converting the array to the read-only interface IEnumerable, which can be safely converted via covariant conversions. +- :grey_question: CONSIDER checking for null before accessing an array rather than assuming there is an array instance. +- :grey_question: CONSIDER checking the array length before indexing into an array rather than assuming the length. +- :grey_question: CONSIDER using the index from end operator (^) rather than Length - 1 with C# 8.0 or higher. +- :heavy_check_mark: DO use parameter arrays when a method can handle any number—including zero—of additional arguments. +- :no_entry: AVOID unsafe array covariance. Instead, CONSIDER converting the array to the read-only interface IEnumerable, which can be safely converted via covariant conversions. ### Assemblies -- :heavy_check_mark: DO: apply AssemblyVersionAttribute to assemblies with public types. -- :grey_question: CONSIDER: applying AssemblyFileVersionAttribute and AssemblyCopyrightAttribute to provide additional information about the assembly. -- :heavy_check_mark: DO: apply the following information assembly attributes: System.Reflection.AssemblyCompanyAttribute, System.Reflection.AssemblyCopyrightAttribute, System.Reflection.AssemblyDescriptionAttribute, and System.Reflection.AssemblyProductAttribute. -- :heavy_check_mark: DO: name custom attribute classes with the suffix Attribute. -- :heavy_check_mark: DO: provide get-only properties (without public setters) on attributes with required property values. -- :heavy_check_mark: DO: provide constructor parameters to initialize properties on attributes with required properties. Each parameter should have the same name (albeit with different casing) as the corresponding property. -- :heavy_check_mark: DO: apply the AttributeUsageAttribute class to custom attributes. - +- :heavy_check_mark: DO apply AssemblyVersionAttribute to assemblies with public types. +- :grey_question: CONSIDER applying AssemblyFileVersionAttribute and AssemblyCopyrightAttribute to provide additional information about the assembly. +- :heavy_check_mark: DO apply the following information assembly attributes: System.Reflection.AssemblyCompanyAttribute, System.Reflection.AssemblyCopyrightAttribute, System.Reflection.AssemblyDescriptionAttribute, and System.Reflection.AssemblyProductAttribute. +- :heavy_check_mark: DO name custom attribute classes with the suffix Attribute. +- :heavy_check_mark: DO provide get-only properties (without public setters) on attributes with required property values. +- :heavy_check_mark: DO provide constructor parameters to initialize properties on attributes with required properties. Each parameter should have the same name (albeit with different casing) as the corresponding property. +- :heavy_check_mark: DO apply the AttributeUsageAttribute class to custom attributes. + ### Branches -- :grey_question: CONSIDER: using an if-else statement instead of an overly complicated conditional expression. -- :grey_question: CONSIDER: refactoring the method to make the control flow easier to understand if you find yourself writing for loops with complex conditionals and multiple loop variables. -- :heavy_check_mark: DO: use the for loop when the number of loop iterations is known in advance and the “counter” that gives the number of iterations executed is needed in the loop. -- :heavy_check_mark: DO: use the while loop when the number of loop iterations is not known in advance and a counter is not needed. -- :x: DO NOT: use continue as the jump statement that exits a switch section. This is legal when the switch is inside a loop, but it is easy to become confused about the meaning of break in a later switch section. -- :no_entry: AVOID: using goto. +- :grey_question: CONSIDER using an if-else statement instead of an overly complicated conditional expression. +- :grey_question: CONSIDER refactoring the method to make the control flow easier to understand if you find yourself writing for loops with complex conditionals and multiple loop variables. +- :heavy_check_mark: DO use the for loop when the number of loop iterations is known in advance and the “counter” that gives the number of iterations executed is needed in the loop. +- :heavy_check_mark: DO use the while loop when the number of loop iterations is not known in advance and a counter is not needed. +- :x: DO NOT use continue as the jump statement that exits a switch section. This is legal when the switch is inside a loop, but it is easy to become confused about the meaning of break in a later switch section. +- :no_entry: AVOID using goto. + ### Classes -- :heavy_check_mark: DO: implement IDisposable to support possible deterministic finalization on classes with finalizers. -- :heavy_check_mark: DO: place multiple generic semantically equivalent classes into a single file if they differ only by the number of generic parameters. -- :heavy_check_mark: DO: pass the instance of the class as the value of the sender for nonstatic events. -- :x: DO NOT: unnecessarily replicate existing managed classes that already perform the function of the unmanaged API. +- :heavy_check_mark: DO implement IDisposable to support possible deterministic finalization on classes with finalizers. +- :heavy_check_mark: DO place multiple generic semantically equivalent classes into a single file if they differ only by the number of generic parameters. +- :no_entry: AVOID shadowing a type parameter of an outer type with an identically named type parameter of a nested type. +- :heavy_check_mark: DO pass the instance of the class as the value of the sender for nonstatic events. +- :x: DO NOT unnecessarily replicate existing managed classes that already perform the function of the unmanaged API. + ### Conversions -- :no_entry: AVOID: direct enum/string conversions where the string must be localized into the user’s language. -- :x: DO NOT: provide an implicit conversion operator if the conversion is lossy. -- :x: DO NOT: throw exceptions from implicit conversions. +- :no_entry: AVOID direct enum/string conversions where the string must be localized into the user’s language. +- :x: DO NOT provide an implicit conversion operator if the conversion is lossy. +- :x: DO NOT throw exceptions from implicit conversions. ### Dispose() -- :heavy_check_mark: DO: implement the dispose pattern on objects with resources that are scarce or expensive. -- :heavy_check_mark: DO: unregister any AppDomain.ProcessExit events during dispose. -- :heavy_check_mark: DO: call System.GC.SuppressFinalize() from Dispose() to avoid repeating resource cleanup and delaying garbage collection on an object. -- :heavy_check_mark: DO: ensure that Dispose() is idempotent (it should be possible to call Dispose() multiple times). -- :heavy_check_mark: DO: keep Dispose() simple, focusing on the resource cleanup required by finalization. -- :no_entry: AVOID: calling Dispose() on owned objects that have a finalizer. Instead, rely on the finalization queue to clean up the instance. -- :heavy_check_mark: DO: invoke a base class’s Dispose() method when overriding Dispose(). -- :grey_question: CONSIDER: ensuring that an object becomes unusable after Dispose() is called. After an object has been disposed, methods other than Dispose() (which could potentially be called multiple times) should throw an ObjectDisposedException. -- :heavy_check_mark: DO: implement IDisposable on types that own disposable fields (or properties) and dispose of those instances. -- :heavy_check_mark: DO: invoke a base class’s Dispose() method from the Dispose(bool disposing) method if one exists. +- :heavy_check_mark: DO implement the dispose pattern on objects with resources that are scarce or expensive. +- :heavy_check_mark: DO unregister any AppDomain.ProcessExit events during dispose. +- :heavy_check_mark: DO call System.GC.SuppressFinalize() from Dispose() to avoid repeating resource cleanup and delaying garbage collection on an object. +- :heavy_check_mark: DO ensure that Dispose() is idempotent (it should be possible to call Dispose() multiple times). +- :heavy_check_mark: DO keep Dispose() simple, focusing on the resource cleanup required by finalization. +- :no_entry: AVOID calling Dispose() on owned objects that have a finalizer. Instead, rely on the finalization queue to clean up the instance. +- :heavy_check_mark: DO invoke a base class’s Dispose() method when overriding Dispose(). +- :grey_question: CONSIDER ensuring that an object becomes unusable after Dispose() is called. After an object has been disposed, methods other than Dispose() (which could potentially be called multiple times) should throw an ObjectDisposedException. +- :heavy_check_mark: DO implement IDisposable on types that own disposable fields (or properties) and dispose of those instances. +- :heavy_check_mark: DO invoke a base class’s Dispose() method from the Dispose(bool disposing) method if one exists. ### Enums -- :grey_question: CONSIDER: adding new members to existing enums, but keep in mind the compatibility risk. -- :no_entry: AVOID: creating enums that represent an "incomplete" set of values, such as product version numbers. -- :no_entry: AVOID: creating "reserved for future use" values in an enum. -- :no_entry: AVOID: enums that contain a single value. -- :heavy_check_mark: DO: provide a value of 0 (none) for simple enums, knowing that 0 will be the default value when no explicit initialization is provided. -- :heavy_check_mark: DO: use the FlagsAttribute to mark enums that contain flags. -- :heavy_check_mark: DO: provide a None value equal to 0 for all flag enums. -- :no_entry: AVOID: creating flag enums where the zero value has a meaning other than "no flags are set." -- :grey_question: CONSIDER: using the Enumerable.Empty() method instead. +- :grey_question: CONSIDER adding new members to existing enums, but keep in mind the compatibility risk. +- :no_entry: AVOID creating enums that represent an “incomplete” set of values, such as product version numbers. +- :no_entry: AVOID creating “reserved for future use” values in an enum. +- :no_entry: AVOID enums that contain a single value. +- :heavy_check_mark: DO provide a value of 0 (none) for simple enums, knowing that 0 will be the default value when no explicit initialization is provided. +- :heavy_check_mark: DO use the FlagsAttribute to mark enums that contain flags. +- :heavy_check_mark: DO provide a None value equal to 0 for all flag enums. +- :no_entry: AVOID creating flag enums where the zero value has a meaning other than “no flags are set.” +- :grey_question: CONSIDER using the Enumerable.Empty() method instead. ### Equality -- :no_entry: AVOID: using equality conditionals with binary floating-point types. Either subtract the two values and see if their difference is less than a tolerance, or use the decimal type. -- :heavy_check_mark: DO: override the equality operators (Equals(), ==, and !=) and GetHashCode() on value types if equality is meaningful. (Also consider implementing the IEquatable interface.) -- :no_entry: AVOID: overriding the equality-related members on mutable reference types or if the implementation would be significantly slower with such overriding. -- :heavy_check_mark: DO: implement all the equality-related methods when implementing IEquitable. -- :no_entry: AVOID: using the equality comparison operator (==) from within the implementation of the == operator overload. -- :heavy_check_mark: DO: ensure that custom comparison logic produces a consistent “total order.” +- :heavy_check_mark: DO override the equality operators (Equals(), ==, and !=) and GetHashCode() on value types if equality is meaningful. (Also consider implementing the IEquatable interface.) +- :no_entry: AVOID overriding the equality-related members on mutable reference types or if the implementation would be significantly slower with such overriding. +- :heavy_check_mark: DO implement all the equality-related methods when implementing IEquitable. +- :no_entry: AVOID using the equality comparison operator (==) from within the implementation of the == operator overload. +- :heavy_check_mark: DO ensure that custom comparison logic produces a consistent “total order.” ### Exceptions -- :no_entry: AVOID: explicitly throwing exceptions from finally blocks. (Implicitly thrown exceptions resulting from method calls are acceptable.) -- :heavy_check_mark: DO: favor try/finally and avoid using try/catch for cleanup code. -- :heavy_check_mark: DO: throw exceptions that describe which exceptional circumstance occurred, and if possible, how to prevent it. -- :no_entry: AVOID: general catch blocks and replace them with a catch of System.Exception. -- :no_entry: AVOID: catching exceptions for which the appropriate action is unknown. It is better to let an exception go unhandled than to handle it incorrectly. -- :no_entry: AVOID: catching and logging an exception before rethrowing it. Instead, allow the exception to escape until it can be handled appropriately. -- :heavy_check_mark: DO: prefer using an empty throw when catching and rethrowing an exception so as to preserve the call stack. -- :heavy_check_mark: DO: report execution failures by throwing exceptions rather than returning error codes. -- :x: DO NOT: have public members that return exceptions as return values or an out parameter. Throw exceptions to indicate errors; do not use them as return values to indicate errors. -- :x: DO NOT: use exceptions for handling normal, expected conditions; use them for exceptional, unexpected conditions. -- :no_entry: AVOID: throwing exceptions from property getters. -- :heavy_check_mark: DO: preserve the original property value if the property throws an exception. -- :heavy_check_mark: DO: use nameof(value) (which resolves to "value") for the paramName argument when creating ArgumentException() or ArgumentNullException() type exceptions (value" is the implicit name of the parameter on property setters)." -- :x: DO NOT: throw exceptions from finalizer methods. -- :heavy_check_mark: DO: throw an ArgumentException or one of its subtypes if bad arguments are passed to a member. Prefer the most derived exception type (e.g., ArgumentNullException), if applicable. -- :x: DO NOT: throw a System.SystemException or an exception type that derives from it. -- :x: DO NOT: throw a System.Exception, System.NullReferenceException, or System.ApplicationException. -- :heavy_check_mark: DO: use nameof for the paramName argument passed into argument exception types that take such a parameter. Examples of such exceptions include ArgumentException, ArgumentOutOfRangeException, and ArgumentNullException. -- :no_entry: AVOID: exception reporting or logging lower in the call stack. -- :x: DO NOT: over-catch. Exceptions should be allowed to propagate up the call stack unless it is clearly understood how to programmatically address those errors lower in the stack. -- :grey_question: CONSIDER: catching a specific exception when you understand why it was thrown in a given context and can respond to the failure programmatically. -- :no_entry: AVOID: catching System.Exception or System.SystemException except in top-level exception handlers that perform final cleanup operations before rethrowing the exception. -- :heavy_check_mark: DO: use throw; rather than throw exception object inside a catch block. -- :heavy_check_mark: DO: use exception filters to avoid rethrowing an exception from within a catch block. -- :heavy_check_mark: DO: use caution when rethrowing different exceptions. -- :no_entry: AVOID: throwing exceptions from exception filters. -- :no_entry: AVOID: exception filters with logic that might implicitly change over time. -- :no_entry: AVOID: deep exception hierarchies. -- :x: DO NOT: create a new exception type if the exception would not be handled differently than an existing CLR exception. Throw the existing framework exception instead. -- :heavy_check_mark: DO: create a new exception type to communicate a unique program error that cannot be communicated using an existing CLR exception and that can be programmatically handled in a different way than any other existing CLR exception type. -- :heavy_check_mark: DO: provide a parameterless constructor on all custom exception types. Also provide constructors that take a message and an inner exception. -- :heavy_check_mark: DO: make exceptions runtime-serializable. -- :grey_question: CONSIDER: providing exception properties for programmatic access to extra information relevant to the exception. -- :grey_question: CONSIDER: wrapping specific exceptions thrown from the lower layer in a more appropriate exception if the lower-layer exception does not make sense in the context of the higher-layer operation. -- :heavy_check_mark: DO: specify the inner exception when wrapping exceptions. -- :heavy_check_mark: DO: target developers as the audience for exceptions, identifying both the problem and the mechanism to resolve it, where possible. -- :grey_question: CONSIDER: registering an unhandled exception event handler for debugging, logging, and emergency shutdown purposes. -- :heavy_check_mark: DO: use the SetLastErrorAttribute on Windows to turn APIs that use SetLastError error codes into methods that throw Win32Exception. +- :no_entry: AVOID explicitly throwing exceptions from finally blocks. (Implicitly thrown exceptions resulting from method calls are acceptable.) +- :heavy_check_mark: DO favor try/finally and avoid using try/catch for cleanup code. +- :heavy_check_mark: DO throw exceptions that describe which exceptional circumstance occurred, and if possible, how to prevent it. +- :no_entry: AVOID general catch blocks and replace them with a catch of System.Exception. +- :no_entry: AVOID catching exceptions for which the appropriate action is unknown. It is better to let an exception go unhandled than to handle it incorrectly. +- :no_entry: AVOID catching and logging an exception before rethrowing it. Instead, allow the exception to escape until it can be handled appropriately. +- :heavy_check_mark: DO prefer using an empty throw when catching and rethrowing an exception so as to preserve the call stack. +- :heavy_check_mark: DO report execution failures by throwing exceptions rather than returning error codes. +- :x: DO NOT have public members that return exceptions as return values or an out parameter. Throw exceptions to indicate errors; do not use them as return values to indicate errors. +- :x: DO NOT use exceptions for handling normal, expected conditions; use them for exceptional, unexpected conditions. +- :no_entry: AVOID throwing exceptions from property getters. +- :heavy_check_mark: DO preserve the original property value if the property throws an exception. +- :x: DO NOT throw exceptions from finalizer methods. +- :heavy_check_mark: DO throw an ArgumentException or one of its subtypes if bad arguments are passed to a member. Prefer the most derived exception type (e.g., ArgumentNullException), if applicable. +- :x: DO NOT throw a System.SystemException or an exception type that derives from it. +- :x: DO NOT throw a System.Exception, System.NullReferenceException, or System.ApplicationException. +- :heavy_check_mark: DO use nameof for the paramName argument passed into argument exception types that take such a parameter. Examples of such exceptions include ArgumentException, ArgumentOutOfRangeException, and ArgumentNullException. +- :no_entry: AVOID exception reporting or logging lower in the call stack. +- :x: DO NOT over-catch. Exceptions should be allowed to propagate up the call stack unless it is clearly understood how to programmatically address those errors lower in the stack. +- :grey_question: CONSIDER catching a specific exception when you understand why it was thrown in a given context and can respond to the failure programmatically. +- :no_entry: AVOID catching System.Exception or System.SystemException except in top-level exception handlers that perform final cleanup operations before rethrowing the exception. +- :heavy_check_mark: DO use throw; rather than throw inside a catch block. +- :heavy_check_mark: DO use exception filters to avoid rethrowing an exception from within a catch block. +- :heavy_check_mark: DO use caution when rethrowing different exceptions. +- :no_entry: AVOID throwing exceptions from exception filters. +- :no_entry: AVOID exception filters with logic that might implicitly change over time. +- :no_entry: AVOID deep exception hierarchies. +- :x: DO NOT create a new exception type if the exception would not be handled differently than an existing CLR exception. Throw the existing framework exception instead. +- :heavy_check_mark: DO create a new exception type to communicate a unique program error that cannot be communicated using an existing CLR exception and that can be programmatically handled in a different way than any other existing CLR exception type. +- :heavy_check_mark: DO provide a parameterless constructor on all custom exception types. Also provide constructors that take a message and an inner exception. +- :heavy_check_mark: DO name an exception classes with the “Exception” suffix. +- :heavy_check_mark: DO make exceptions runtime-serializable. +- :grey_question: CONSIDER providing exception properties for programmatic access to extra information relevant to the exception. +- :grey_question: CONSIDER wrapping specific exceptions thrown from the lower layer in a more appropriate exception if the lower-layer exception does not make sense in the context of the higher-layer operation. +- :heavy_check_mark: DO specify the inner exception when wrapping exceptions. +- :heavy_check_mark: DO target developers as the audience for exceptions, identifying both the problem and the mechanism to resolve it, where possible. +- :grey_question: CONSIDER registering an unhandled exception event handler for debugging, logging, and emergency shutdown purposes. +- :heavy_check_mark: DO use the SetLastErrorAttribute on Windows to turn APIs that use SetLastError error codes into methods that throw Win32Exception. ### Fields -- :grey_question: CONSIDER: initializing static fields inline rather than explicitly using static constructors or declaration assigned values. -- :heavy_check_mark: DO: use public static readonly modified fields for predefined object instances prior to C# 6.0. -- :no_entry: AVOID: changing a public readonly modified field in pre-C# 6.0 to a read-only automatically implemented property in C# 6.0 (or later) if version API compatibility is required. -- :no_entry: AVOID: publicly exposed nested types. The only exception is if the declaration of such a type is unlikely or pertains to an advanced customization scenario. +- :grey_question: CONSIDER initializing static fields inline rather than explicitly using static constructors or declaration assigned values. +- :heavy_check_mark: DO use public static readonly modified fields for predefined object instances prior to C# 6.0. +- :no_entry: AVOID changing a public readonly modified field in pre-C# 6.0 to a read-only automatically implemented property in C# 6.0 (or later) if version API compatibility is required. +- :no_entry: AVOID publicly exposed nested types. The only exception is if the declaration of such a type is unlikely or pertains to an advanced customization scenario. +- :heavy_check_mark: DO use PascalCasing and an “I” prefix for interface names. + ### Flags -- :grey_question: CONSIDER: using the default 32-bit integer type as the underlying type of an enum. Use a smaller type only if you must do so for interoperability; use a larger type only if you are creating a flags enum with more than 32 flags. -- :grey_question: CONSIDER: providing special values for commonly used combinations of flags. -- :heavy_check_mark: DO: use powers of 2 to ensure that all flag combinations are represented uniquely. +- :grey_question: CONSIDER using the default 32-bit integer type as the underlying type of an enum. Use a smaller type only if you must do so for interoperability; use a larger type only if you are creating a flags enum with more than 32 flags. +- :grey_question: CONSIDER providing special values for commonly used combinations of flags. +- :heavy_check_mark: DO use powers of 2 to ensure that all flag combinations are represented uniquely. ### Increment/decrement -- :no_entry: AVOID: confusing usage of the increment and decrement operators. -- :heavy_check_mark: DO: be cautious when porting code between C, C++, and C# that uses increment and decrement operators; C and C++ implementations need not follow the same rules as C#. +- :no_entry: AVOID confusing usage of the increment and decrement operators. +- :heavy_check_mark: DO be cautious when porting code between C, C++, and C# that uses increment and decrement operators; C and C++ implementations need not follow the same rules as C#. ### Interfaces -- :x: DO NOT: add abstract members to an interface that has already been published. -- :grey_question: CONSIDER: using extension methods or an additional interface in place of default interface members when adding methods to a published interface. -- :heavy_check_mark: DO: use extension methods when the interface providing the polymorphic behavior is not under your control. -- :heavy_check_mark: DO: use an additional interface when properties are necessary for extending polymorphic behavior for framework support prior to .NET Core 3.0. -- :grey_question: CONSIDER: interfaces over abstract classes for polymorphic behavior starting with in C# 8.0/.NET Core 3.0 and abstract classes prior to C# 8.0. -- :grey_question: CONSIDER: defining an interface if you need to support its functionality on types that already inherit from some other type. -- :no_entry: AVOID: using “marker” interfaces with no members; use attributes instead. +- :x: DO NOT add abstract members to an interface that has already been published. +- :grey_question: CONSIDER using extension methods or an additional interface in place of default interface members when adding methods to a published interface. +- :heavy_check_mark: DO use extension methods when the interface providing the polymorphic behavior is not under your control. +- :heavy_check_mark: DO use an additional interface when properties are necessary for extending polymorphic behavior for framework support prior to .NET Core 3.0. +- :grey_question: CONSIDER interfaces over abstract classes for polymorphic behavior starting with in C# 8.0/.NET Core 3.0 and abstract classes prior to C# 8.0. +- :grey_question: CONSIDER defining an interface if you need to support its functionality on types that already inherit from some other type. +- :no_entry: AVOID using “marker” interfaces with no members; use attributes instead. ### Methods -- :no_entry: AVOID: frivolously defining extension methods, especially on types you don’t own. -- :heavy_check_mark: DO: implement GetHashCode(), Equals(), the == operator, and the != operator together—not one of these without the other three. -- :heavy_check_mark: DO: use the same algorithm when implementing Equals(), ==, and !=. -- :x: DO NOT: throw exceptions from implementations of GetHashCode(), Equals(), ==, and !=. -- :heavy_check_mark: DO: implement finalizer methods only on objects with resources that don't have finalizers but still require cleanup. -- :heavy_check_mark: DO: refactor a finalization method to call the same code as IDisposable, perhaps simply by calling the Dispose() method. -- :grey_question: CONSIDER: terminating the process by calling System.Environment.FailFast() if the program encounters a scenario where it is unsafe to continue execution. -- :no_entry: AVOID: misleading the caller with generic methods that are not as type-safe as they appear. -- :no_entry: AVOID: the anonymous method syntax in new code; prefer the more compact lambda expression syntax. -- :heavy_check_mark: DO: use the null-conditional operator prior to calling Invoke() starting in C# 6.0. -- :heavy_check_mark: DO: use System.Linq.Enumerable.Any() rather than calling patents.Count() when checking whether there are more than zero items. -- :heavy_check_mark: DO: use a collection’s Count property (if available) instead of calling the System.Linq.Enumerable.Count() method. -- :x: DO NOT: call an OrderBy() following a prior OrderBy() method call. Use ThenBy() to sequence items by more than one value. -- :grey_question: CONSIDER: using the standard query operators (method call form) if the query involves operations that do not have a query expression syntax, such as Count(), TakeWhile(), or Distinct(). -- :heavy_check_mark: DO: create public managed wrappers around unmanaged methods that use the conventions of managed code, such as structured exception handling. -- :heavy_check_mark: DO: declare extern methods as private or internal. -- :heavy_check_mark: DO: provide public wrapper methods that use managed conventions such as structured exception handling, use of enums for special values, and so on. -- :heavy_check_mark: DO: extend SafeHandle or implement IDisposable and create a finalizer to ensure that unmanaged resources can be cleaned up effectively. +- :no_entry: AVOID frivolously defining extension methods, especially on types you don’t own. +- :heavy_check_mark: DO implement GetHashCode(), Equals(), the == operator, and the != operator together—not one of these without the other three. +- :heavy_check_mark: DO use the same algorithm when implementing Equals(), ==, and !=. +- :x: DO NOT throw exceptions from implementations of GetHashCode(), Equals(), ==, and !=. +- :heavy_check_mark: DO implement finalizer methods only on objects with resources that don't have finalizers but still require cleanup. +- :heavy_check_mark: DO refactor a finalization method to call the same code as IDisposable, perhaps simply by calling the Dispose() method. +- :grey_question: CONSIDER terminating the process by calling System.Environment.FailFast() if the program encounters a scenario where it is unsafe to continue execution. +- :no_entry: AVOID misleading the caller with generic methods that are not as type-safe as they appear. +- :no_entry: AVOID the anonymous method syntax in new code; prefer the more compact lambda expression syntax. +- :heavy_check_mark: DO use the null-conditional operator prior to calling Invoke() starting in C# 6.0. +- :heavy_check_mark: DO use System.Linq.Enumerable.Any() rather than calling patents.Count() when checking whether there are more than zero items. +- :heavy_check_mark: DO use a collection’s Count property (if available) instead of calling the System.Linq.Enumerable.Count() method. +- :x: DO NOT call an OrderBy() following a prior OrderBy() method call. Use ThenBy() to sequence items by more than one value. +- :grey_question: CONSIDER using the standard query operators (method call form) if the query involves operations that do not have a query expression syntax, such as Count(), TakeWhile(), or Distinct(). +- :heavy_check_mark: DO create public managed wrappers around unmanaged methods that use the conventions of managed code, such as structured exception handling. +- :heavy_check_mark: DO declare extern methods as private or internal. +- :heavy_check_mark: DO provide public wrapper methods that use managed conventions such as structured exception handling, use of enums for special values, and so on. +- :heavy_check_mark: DO extend SafeHandle or implement IDisposable and create a finalizer to ensure that unmanaged resources can be cleaned up effectively. -### Parameters -- :heavy_check_mark: DO: provide good defaults for all parameters where possible. -- :heavy_check_mark: DO: provide simple method overloads that have a small number of required parameters. -- :grey_question: CONSIDER: organizing overloads from the simplest to the most complex. -- :heavy_check_mark: DO: provide constructor optional parameters and/or convenience constructor overloads that initialize properties with good defaults. -- :heavy_check_mark: DO: simplify the wrapper methods by choosing default values for unnecessary parameters. -- :no_entry: AVOID: providing constructor parameters to initialize attribute properties corresponding to the optional arguments (and, therefore, avoid overloading custom attribute constructors). +### Miscellaneous +- :heavy_check_mark: DO favor consistency rather than variety within your code. +- :heavy_check_mark: DO rely on System.WriteLine() and System.Environment.NewLine rather than \n to accommodate Windows-specific operating system idiosyncrasies with the same code that runs on Linux and iOS. +- :grey_question: CONSIDER registering the finalization code with the AppDomain.ProcessExit to increase the probability that resource cleanup will execute before the process exits. +- :no_entry: AVOID referencing other objects that are not being finalized during finalization. +- :no_entry: AVOID capturing loop variables in anonymous functions. +- :heavy_check_mark: DO check that the value of a delegate is not null before invoking it. +- :heavy_check_mark: DO check that the value of a delegate is not null before invoking it (possibly by using the null-conditional operator in C# 6.0). +- :heavy_check_mark: DO pass null as the sender for static events. +- :x: DO NOT pass null as the value of the eventArgs argument. +- :heavy_check_mark: DO use query expression syntax to make queries easier to read, particularly if they involve complex from, let, join, or group clauses. +- :x: DO NOT make any unwarranted assumptions about the order in which elements of a collection will be enumerated. If the collection is not documented as enumerating its elements in a particular order, it is not guaranteed to produce elements in any particula +- :x: DO NOT represent an empty collection with a null reference. +- :grey_question: CONSIDER using nonrecursive algorithms when iterating over potentially deep data structures. +- :heavy_check_mark: DO use delegate types that match the signature of the desired method when an unmanaged API requires a function pointer. +- :heavy_check_mark: DO use ref parameters rather than pointer types when possible. +### Parameters +- :heavy_check_mark: DO provide good defaults for all parameters where possible. +- :heavy_check_mark: DO provide simple method overloads that have a small number of required parameters. +- :grey_question: CONSIDER organizing overloads from the simplest to the most complex. +- :heavy_check_mark: DO provide constructor optional parameters and/or convenience constructor overloads that initialize properties with good defaults. +- :no_entry: AVOID providing constructor parameters to initialize attribute properties corresponding to the optional arguments (and, therefore, avoid overloading custom attribute constructors). +- :heavy_check_mark: DO simplify the wrapper methods by choosing default values for unnecessary parameters. + ### Properties -- :heavy_check_mark: DO: use properties for simple access to simple data with simple computations. -- :heavy_check_mark: DO: favor automatically implemented properties over properties with simple backing fields when no additional logic is required. -- :heavy_check_mark: DO: favor automatically implemented properties over fields. -- :heavy_check_mark: DO: favor automatically implemented properties over using fully expanded ones if there is no additional implementation logic. -- :no_entry: AVOID: accessing the backing field of a property outside the property, even from within the containing class. -- :heavy_check_mark: DO: create read-only properties if the property value should not be changed. -- :heavy_check_mark: DO: create read-only automatically implemented properties in C# 6.0 (or later) rather than read-only properties with a backing field if the property value should not be changed. -- :heavy_check_mark: DO: apply appropriate accessibility modifiers on implementations of getters and setters on all properties. -- :x: DO NOT: provide set-only properties or properties with the setter having broader accessibility than the getter. -- :heavy_check_mark: DO: provide sensible defaults for all properties, ensuring that defaults do not result in a security hole or significantly inefficient code. -- :heavy_check_mark: DO: allow properties to be set in any order, even if this results in a temporarily invalid object state. -- :heavy_check_mark: DO: implement non-nullable read/write reference fully implemented properties with a nullable backing field, a null-forgiveness operator when returning the field from the getter, and non-null validation in the property setter. -- :heavy_check_mark: DO: assign non-nullable reference-type properties before instantiation completes. -- :heavy_check_mark: DO: implement non-nullable reference-type automatically implemented properties as read-only. -- :heavy_check_mark: DO: use a nullable check for all reference-type properties and fields that are not initialized before instantiation completes. -- :heavy_check_mark: DO: favor read-only automatically implemented properties in C# 6.0 (or later) over read-only fields. +- :heavy_check_mark: DO use properties for simple access to simple data with simple computations. +- :heavy_check_mark: DO favor automatically implemented properties over properties with simple backing fields when no additional logic is required. +- :grey_question: CONSIDER using the same casing on a property’s backing field as that used in the property, distinguishing the backing field with an “_” prefix. Do not, however, use two underscores; identifiers beginning with two underscores are reserved for the use of t +- :heavy_check_mark: DO favor automatically implemented properties over fields. +- :heavy_check_mark: DO favor automatically implemented properties over using fully expanded ones if there is no additional implementation logic. +- :no_entry: AVOID accessing the backing field of a property outside the property, even from within the containing class. +- :heavy_check_mark: DO use nameof(value) (which resolves to “value”) for the paramName argument when creating ArgumentException() or ArgumentNullException() type exceptions (value"" is the implicit name of the parameter on property setters). +- :heavy_check_mark: DO create read-only properties if the property value should not be changed. +- :heavy_check_mark: DO create read-only automatically implemented properties in C# 6.0 (or later) rather than read-only properties with a backing field if the property value should not be changed. +- :heavy_check_mark: DO apply appropriate accessibility modifiers on implementations of getters and setters on all properties. +- :x: DO NOT provide set-only properties or properties with the setter having broader accessibility than the getter. +- :heavy_check_mark: DO provide sensible defaults for all properties, ensuring that defaults do not result in a security hole or significantly inefficient code. +- :heavy_check_mark: DO allow properties to be set in any order, even if this results in a temporarily invalid object state. +- :heavy_check_mark: DO allow properties to be set in any order, even if this results in a temporarily invalid object state. +- :heavy_check_mark: DO implement non-nullable read/write reference fully implemented properties with a nullable backing field, a null-forgiveness operator when returning the field from the getter, and non-null validation in the property setter. +- :heavy_check_mark: DO assign non-nullable reference-type properties before instantiation completes. +- :heavy_check_mark: DO implement non-nullable reference-type automatically implemented properties as read-only. +- :heavy_check_mark: DO use a nullable check for all reference-type properties and fields that are not initialized before instantiation completes. +- :heavy_check_mark: DO favor read-only automatically implemented properties in C# 6.0 (or later) over read-only fields. ### Strings -- :heavy_check_mark: DO: favor composite formatting over use of the addition operator for concatenating strings when localization is a possibility. +- :heavy_check_mark: DO favor composite formatting over use of the addition operator for concatenating strings when localization is a possibility. ### Structs -- :heavy_check_mark: DO: ensure that the default value of a struct is valid; encapsulation cannot prevent obtaining the default “all zero” value of a struct. -- :x: DO NOT: define a struct unless it logically represents a single value, consumes 16 bytes or less of storage, is immutable, and is infrequently boxed. +- :heavy_check_mark: DO ensure that the default value of a struct is valid; encapsulation cannot prevent obtaining the default “all zero” value of a struct. +- :x: DO NOT define a struct unless it logically represents a single value, consumes 16 bytes or less of storage, is immutable, and is infrequently boxed. ### Synchronization -- :heavy_check_mark: DO: declare a separate, read-only synchronization variable of type object for the synchronization target. -- :no_entry: AVOID: using the MethodImplAttribute for synchronization. -- :x: DO NOT: request exclusive ownership of the same two or more synchronization targets in different orders. -- :heavy_check_mark: DO: encapsulate mutable static data in public APIs with synchronization logic. -- :no_entry: AVOID: synchronization on simple reading or writing of values no bigger than a native (pointer-size) integer, as such operations are automatically atomic. +- :heavy_check_mark: DO declare a separate, read-only synchronization variable of type object for the synchronization target. +- :no_entry: AVOID using the MethodImplAttribute for synchronization. +- :x: DO NOT request exclusive ownership of the same two or more synchronization targets in different orders. +- :heavy_check_mark: DO encapsulate mutable static data in public APIs with synchronization logic. +- :no_entry: AVOID synchronization on simple reading or writing of values no bigger than a native (pointer-size) integer, as such operations are automatically atomic. ### Tasks -- :heavy_check_mark: DO: cancel unfinished tasks rather than allowing them to run during application shutdown. -- :heavy_check_mark: DO: inform the task factory that a newly created task is likely to be long-running so that it can manage it appropriately. -- :heavy_check_mark: DO: use TaskCreationOptions.LongRunning sparingly. -- :heavy_check_mark: DO: use tasks and related APIs in favor of System.Theading classes such as Thread and ThreadPool. +- :heavy_check_mark: DO cancel unfinished tasks rather than allowing them to run during application shutdown. +- :heavy_check_mark: DO cancel unfinished tasks rather than allowing them to run during application shutdown. +- :heavy_check_mark: DO inform the task factory that a newly created task is likely to be long-running so that it can manage it appropriately. +- :heavy_check_mark: DO use TaskCreationOptions.LongRunning sparingly. +- :heavy_check_mark: DO use tasks and related APIs in favor of System.Theading classes such as Thread and ThreadPool. ### Threads -- :x: DO NOT: fall into the common error of believing that more threads always make code faster. -- :heavy_check_mark: DO: carefully measure performance when attempting to speed up processor-bound problems through multithreading. -- :x: DO NOT: make an unwarranted assumption that any operation that is seemingly atomic in single-threaded code will be atomic in multithreaded code. -- :x: DO NOT: assume that all threads will observe all side effects of operations on shared memory in a consistent order. -- :heavy_check_mark: DO: ensure that code that concurrently acquires multiple locks always acquires them in the same order. -- :no_entry: AVOID: all race conditions—that is, conditions where program behavior depends on how the operating system chooses to schedule threads. -- :no_entry: AVOID: writing programs that produce unhandled exceptions on any thread. -- :no_entry: AVOID: calling Thread.Sleep() in production code. -- :heavy_check_mark: DO: use parallel loops when the computations performed can be easily split up into many mutually independent processor-bound computations that can be executed in any order on any thread. -- :no_entry: AVOID: locking on this, System.Type, or a string. -- :heavy_check_mark: DO: ensure that code that concurrently holds multiple locks always acquires them in the same order. +- :x: DO NOT fall into the common error of believing that more threads always make code faster. +- :heavy_check_mark: DO carefully measure performance when attempting to speed up processor-bound problems through multithreading. +- :x: DO NOT make an unwarranted assumption that any operation that is seemingly atomic in single-threaded code will be atomic in multithreaded code. +- :x: DO NOT assume that all threads will observe all side effects of operations on shared memory in a consistent order. +- :heavy_check_mark: DO ensure that code that concurrently acquires multiple locks always acquires them in the same order. +- :no_entry: AVOID all race conditions—that is, conditions where program behavior depends on how the operating system chooses to schedule threads. +- :no_entry: AVOID writing programs that produce unhandled exceptions on any thread. +- :no_entry: AVOID calling Thread.Sleep() in production code. +- :heavy_check_mark: DO use parallel loops when the computations performed can be easily split up into many mutually independent processor-bound computations that can be executed in any order on any thread. +- :no_entry: AVOID locking on this, System.Type, or a string. +- :heavy_check_mark: DO ensure that code that concurrently holds multiple locks always acquires them in the same order. ### ToString() -- :heavy_check_mark: DO: override ToString() whenever useful developer-oriented diagnostic strings can be returned. -- :grey_question: CONSIDER: trying to keep the string returned from ToString() short. -- :x: DO NOT: return an empty string or null from ToString(). -- :x: DO NOT: throw exceptions or make observable side effects (change the object state) from ToString(). +- :heavy_check_mark: DO override ToString() whenever useful developer-oriented diagnostic strings can be returned. +- :grey_question: CONSIDER trying to keep the string returned from ToString() short. +- :x: DO NOT return an empty string or null from ToString(). +- :x: DO NOT throw exceptions or make observable side effects (change the object state) from ToString(). - :heavy_check_mark: DO provide an overloaded ToString(string format) or implement IFormattable if the return value requires formatting or is culture-sensitive (e.g., DateTime). -- :grey_question: CONSIDER: returning a unique string from ToString() so as to identify the object instance. +- :grey_question: CONSIDER returning a unique string from ToString() so as to identify the object instance. ### Types -- :no_entry: AVOID: using implicitly typed local variables unless the data type of the assigned value is obvious. -- :no_entry: AVOID: binary floating-point types when exact decimal arithmetic is required; use the decimal floating-point type instead. -- :no_entry: AVOID: creating value types that consume more than 16 bytes of memory. -- :heavy_check_mark: DO: make value types immutable. -- :no_entry: AVOID: mutable value types. -- :no_entry: AVOID: implementing multiple constructions of the same generic interface in one type. -- :grey_question: CONSIDER: whether the readability benefit of defining your own delegate type outweighs the convenience of using a predefined generic delegate type. -- :grey_question: CONSIDER: omitting the types from lambda formal parameter lists when the types are obvious to the reader or when they are an insignificant detail. -- :heavy_check_mark: DO: use System.EventArgs or a type that derives from System.EventArgs for a TEventArgs type. -- :grey_question: CONSIDER: using a subclass of System.EventArgs as the event argument type (TEventArgs) unless you are sure the event will never need to carry any data. -- :grey_question: CONSIDER: using System.EventHandler instead of manually creating new delegate types for event handlers unless the parameter names of a custom type offer significant clarification. +- :no_entry: AVOID using implicitly typed local variables unless the data type of the assigned value is obvious. +- :no_entry: AVOID binary floating-point types when exact decimal arithmetic is required; use the decimal floating-point type instead. +- :no_entry: AVOID using equality conditionals with binary floating-point types. Either subtract the two values and see if their difference is less than a tolerance, or use the decimal type. +- :no_entry: AVOID creating value types that consume more than 16 bytes of memory. +- :heavy_check_mark: DO make value types immutable. +- :no_entry: AVOID mutable value types. +- :no_entry: AVOID implementing multiple constructions of the same generic interface in one type. +- :grey_question: CONSIDER whether the readability benefit of defining your own delegate type outweighs the convenience of using a predefined generic delegate type. +- :grey_question: CONSIDER omitting the types from lambda formal parameter lists when the types are obvious to the reader or when they are an insignificant detail. +- :heavy_check_mark: DO use System.EventArgs or a type that derives from System.EventArgs for a TEventArgs type. +- :grey_question: CONSIDER using a subclass of System.EventArgs as the event argument type (TEventArgs) unless you are sure the event will never need to carry any data. +- :grey_question: CONSIDER using System.EventHandler instead of manually creating new delegate types for event handlers unless the parameter names of a custom type offer significant clarification. + + + + + +## ***Layout*** + + + + + + + +### Comments +- :x: DO NOT use comments unless they describe something that is not obvious to someone other than the developer who wrote the code. +- :heavy_check_mark: DO favor writing clearer code over entering comments to clarify a complicated algorithm. +- :heavy_check_mark: DO provide XML comments on public APIs when they provide more context than the API signature alone. This includes member descriptions, parameter descriptions, and examples of calling the API. + + + + + + + +### Files +- :grey_question: CONSIDER organizing the directory hierarchy for source code files to match the namespace hierarchy. +- :x: DO NOT place more than one class in a single source file. +- :grey_question: CONSIDER creating a folder structure that matches the namespace hierarchy. + + + + + + + + + + + + + + + + + +### Whitespace +- :heavy_check_mark: DO use parentheses to make code more readable, particularly if the operator precedence is not clear to the casual reader. +- :no_entry: AVOID omitting braces, except for the simplest of single-line if statements. + + + +## ***Naming*** + +### Abbreviations +- :x: DO NOT use abbreviations or contractions within identifier names. +- :x: DO NOT use any acronyms unless they are widely accepted, and even then use them consistently. + + + + +### Casing +- :heavy_check_mark: DO capitalize both characters in two-character acronyms, except for the first word of a camelCased identifier. +- :heavy_check_mark: DO capitalize only the first character in acronyms with three or more characters, except for the first word of a camelCased identifier. +- :x: DO NOT capitalize any of the characters in acronyms at the beginning of a camelCased identifier. +- :heavy_check_mark: DO use PascalCasing for all class names. +- :heavy_check_mark: DO use camelCasing for local variable names. +- :heavy_check_mark: DO use uppercase literal suffixes (e.g., 1.618033988749895M). +- :heavy_check_mark: DO use camelCasing for variable declarations using tuple syntax. +- :grey_question: CONSIDER using PascalCasing for all tuple item names. +- :heavy_check_mark: DO use PascalCasing for namespace names. +- :heavy_check_mark: DO use camelCasing for parameter names. +- :no_entry: AVOID naming fields with camelCase. +- :heavy_check_mark: DO favor prefixing Boolean properties with “Is,” “Can,” or “Has,” when that practice adds value. +- :heavy_check_mark: DO name properties with PascalCase. + + + + + + + + + + + + + +### Methods +- :heavy_check_mark: DO give methods names that are verbs or verb phrases. ### Miscellaneous -- :heavy_check_mark: DO: favor consistency rather than variety within your code. -- :heavy_check_mark: DO: rely on System.WriteLine() and System.Environment.NewLine rather than \n to accommodate Windows-specific operating system idiosyncrasies with the same code that runs on Linux and iOS. -- :grey_question: CONSIDER: registering the finalization code with the AppDomain.ProcessExit to increase the probability that resource cleanup will execute before the process exits. -- :no_entry: AVOID: referencing other objects that are not being finalized during finalization. -- :no_entry: AVOID: capturing loop variables in anonymous functions. -- :heavy_check_mark: DO: check that the value of a delegate is not null before invoking it (possibly by using the null-conditional operator in C# 6.0). -- :heavy_check_mark: DO: pass null as the sender for static events. -- :x: DO NOT: pass null as the value of the eventArgs argument. -- :heavy_check_mark: DO: use query expression syntax to make queries easier to read, particularly if they involve complex from, let, join, or group clauses. -- :x: DO NOT: make any unwarranted assumptions about the order in which elements of a collection will be enumerated. If the collection is not documented as enumerating its elements in a particular order, it is not guaranteed to produce elements in any particula -- :x: DO NOT: represent an empty collection with a null reference. -- :grey_question: CONSIDER: using nonrecursive algorithms when iterating over potentially deep data structures. -- :heavy_check_mark: DO: use delegate types that match the signature of the desired method when an unmanaged API requires a function pointer. -- :heavy_check_mark: DO: use ref parameters rather than pointer types when possible. +- :heavy_check_mark: DO treat parameter names as part of the API, and avoid changing the names if version compatibility between APIs is important. +- :heavy_check_mark: DO name the source file with the name of the public type it contains. +- :grey_question: CONSIDER giving a property the same name as its type. +- :heavy_check_mark: DO use the same name for constructor parameters (camelCase) and properties (PascalCase) if the constructor parameters are used to simply set the property. + +### Namespaces +- :heavy_check_mark: DO prefix namespace names with a company name to prevent namespaces from different companies having the same name. +- :heavy_check_mark: DO use a stable, version-independent product name at the second level of a namespace name. + + + + + + + + + +### Types +- :x: DO NOT define types without placing them into a namespace. +- :heavy_check_mark: DO choose meaningful names for type parameters and prefix the name with T. +- :grey_question: CONSIDER indicating a constraint in the name of a type parameter. + +### Variables and fields +- :heavy_check_mark: DO favor clarity over brevity when naming identifiers. +- :x: DO NOT use Hungarian notation (that is, do not encode the type of a variable in its name). +- :heavy_check_mark: DO name classes with nouns or noun phrases. +- :heavy_check_mark: DO use the C# keyword rather than the BCL name when specifying a data type (e.g., string rather than String). +- :x: DO NOT use a constant for any value that can possibly change over time. The value of pi and the number of protons in an atom of gold are constants; the price of gold, the name of your company, and the version number of your program can change. +- :heavy_check_mark: DO name properties using a noun, noun phrase, or adjective. +- :heavy_check_mark: DO declare all instance fields as private (and expose them via a property). +- :heavy_check_mark: DO use constant fields for values that will never change. +- :no_entry: AVOID constant fields for values that will change over time. +- :x: DO NOT include “sentinel” values (such as a value called Maximum); such values can be confusing to the user. + +