AlgoMaster Logo

Comments & XML Documentation

Last Updated: May 17, 2026

9 min read

Comments are notes you leave inside source code that the compiler ignores. C# has three flavors: single-line, multi-line, and a third one called XML documentation comments that the compiler actually reads and turns into structured docs. This lesson covers all three, the common XML tags, how the generated docs power IntelliSense and external tools, and the habits that keep your comments useful instead of misleading.

Single-Line Comments

Two forward slashes start a comment that runs to the end of the line. Anything after // on the same line is for humans, not the compiler.

Two common spots for //: a short note above a line or block explaining intent, and a trailing note on the same line for a quick clarification. Either is fine. The trailing form gets noisy if you stack three or four of them in a row, so move to the line-above form when that happens.

You'll also see // used to temporarily disable a line during debugging. That's a normal short-term move. The bad habit is leaving the disabled line behind after you've fixed the bug. We'll come back to that under good and bad practices.

Multi-Line Comments

A multi-line comment starts with /* and ends with */. Everything between the markers, including line breaks, is a comment.

The leading * on each interior line is a convention, not a requirement. It makes the block easier to scan but the compiler does not care.

One thing to remember: /* ... */ does not nest. If you try to wrap an already-commented block in another /* ... */, the inner */ ends the outer comment early and the rest becomes code again, usually triggering a confusing compile error.

What's wrong with this code?

The first */ (on line 2) closes the outer comment. The line still part of outer? is then parsed as C# code and the trailing */ becomes a stray token. The compiler reports CS1525: Invalid expression term '*' or similar near that line.

Fix: Comment the block out with // on each line, or simply delete the inner markers. For commenting out larger blocks of code, most editors offer a "toggle comment" command that uses // on every selected line, which sidesteps the nesting problem entirely.

In practice, most modern C# code uses // for everything and reaches for /* ... */ rarely. The one place /* */ still shows up is inline within an expression, like Process(/* batchSize */ 100, /* retries */ 3) to label a positional argument. Even there, C# now supports named arguments (Process(batchSize: 100, retries: 3)) which read better.

XML Documentation Comments

Three forward slashes (///) start an XML documentation comment. These look like comments, but the compiler treats them as structured data attached to whatever member they sit above. You write the content as XML, and tools downstream consume it.

Here's a Product class with XML docs on the type and its members:

A few things to notice. Each /// line is one row of the XML block. The tags (<summary>, <remarks>, <param>) describe different aspects of the member. The compiler validates that <param name="..."> matches a real parameter and warns when it does not. IDE tooltips read these tags and show them when you hover over Product or its members.

Here is a method with the full set of common tags:

Three things worth pointing out. <paramref name="subtotal"/> is the in-text version of <param>, used to reference a parameter from inside another tag's prose. <exception cref="..."> documents which exception types the method can throw; the cref attribute is checked at compile time and breaks if you rename or remove the exception class. <example> and its inner <code> block are what IDE tooltips render as a usage snippet.

Common XML Documentation Tags

Most XML docs use a small set of tags. This table is the working reference.

TagPurpose
<summary>One-sentence description shown in tooltips. The most important tag.
<remarks>Extended details, edge cases, or design notes. Shown when the user expands the tooltip.
<param name="x">Describes a parameter. Compiler validates the name against the real signature.
<returns>Describes the return value. Omit for void methods.
<exception cref="...">Documents an exception the method may throw. cref is resolved at compile time.
<example>Wraps a usage example. Usually contains a <code> block.
<code>A block of example code, rendered as preformatted text.
<c>A short inline code fragment, for example <c>null</c>.
<see cref="..."/>Inline link to another type or member. Becomes a clickable link in rendered docs.
<seealso cref="..."/>"See also" link, typically rendered in a list at the bottom of a member's doc.
<paramref name="x"/>Inline reference to a parameter from prose inside another tag.
<typeparam name="T">Describes a generic type parameter on a generic class or method.
<typeparamref name="T"/>Inline reference to a type parameter from prose.

Here is a generic class using <typeparam>:

The <typeparam name="T"> description appears in the IDE when you hover over the generic parameter or instantiate the class.

How XML Docs Are Generated and Consumed

XML doc comments live in the source file, but their real power comes from what tooling does with them. There are three consumers.

The first is IntelliSense. When you type cart.CalculateDiscount( in any C# IDE, a tooltip pops up showing the <summary> and the <param> text for each parameter. The IDE reads this directly from source for code in the same project, and from a generated .xml file for code from referenced libraries.

The second is the generated XML file. The C# compiler can emit a .xml file alongside the compiled .dll, containing all the documentation comments in machine-readable form. You enable it in your .csproj:

Build the project and you'll find both Cart.dll and Cart.xml in bin/Debug/net8.0/. The .xml looks roughly like this:

When someone references your library through NuGet, this file ships next to the DLL and powers their IDE's IntelliSense.

The third consumer is documentation site generators. Tools like DocFX and Sandcastle read the same XML file and turn it into a browsable doc site, complete with cross-references, inheritance trees, and search. This is how libraries like ASP.NET Core and Entity Framework Core publish their API reference pages.

The diagram traces what happens when you build a project with documentation enabled. The compiler reads /// comments and produces two artifacts: the .dll with your code and a parallel .xml with your docs. The .xml is what IDEs and doc generators consume downstream.

One side effect of enabling GenerateDocumentationFile is that you'll start seeing warning CS1591: Missing XML comment for publicly visible type or member. The compiler warns once per undocumented public member. This is intentional, the assumption is that if you turn on doc generation, you want every public surface documented. You can suppress it project-wide by adding <NoWarn>$(NoWarn);CS1591</NoWarn> to the .csproj, but a better fix is to actually document the public API and let the warnings guide you to the gaps.

Good and Bad Commenting Practices

Comments are easy to write and easy to get wrong. A few habits separate useful comments from harmful ones.

Comment WHY, not WHAT. The code already tells the reader what it does. The comment should explain why it does it that way, especially when the choice isn't obvious.

Bad:

Better:

The first version restates the code. The second version explains a fact the code itself cannot capture: where the 1.08 came from. Six months later, when the rate changes, the second comment tells you which tax this is and prompts you to look up the new value.

Do not comment obvious code. A loop named foreach (var item in cart.Items) does not need a comment that says // loop through cart items. Trust the reader to read C#.

Keep comments updated. A comment that lies is worse than no comment. When you change the code, fix the comment in the same edit. A comment that says // always returns a non-null Product while the code returns null on missing input will mislead the next person to read it. Stale comments accumulate in old codebases and slowly poison trust in every other comment.

Avoid commented-out code. Deleting code feels permanent, so it's tempting to leave the old version commented out "just in case." Version control already keeps every prior version, so the commented block is dead weight. Delete it. If you genuinely need it back, git log is one command away.

Use TODO and FIXME conventions. A // TODO: marker for a known unfinished piece of work, or a // FIXME: marker for a known bug, is a useful signal. IDEs index these and let you list every TODO in a project. Keep them short and add the date or the issue number when possible:

The reference to issue #412 ties the TODO to a tracked piece of work, so it does not become permanent debris.

Public APIs deserve XML docs. For internal helper methods, a one-line // comment is often enough. For anything public in a library or shared assembly, write XML docs. The cost is a few extra lines; the benefit is that anyone calling your code gets IntelliSense, and any external doc site has something to render.

What's wrong with this comment?

The comment is technically true but adds no information the signature does not already convey. A reader can see "returns decimal" and "calculates total" from the name and return type alone. What the comment does not explain is that the tax is applied after summing, that quantities are factored in, or what units taxRate expects. An XML doc comment with <summary>, <param name="taxRate">, and <returns> would do the job properly.

Fix:

Region Directives

C# has a #region / #endregion directive pair that creates a foldable block in IDEs.

Regions are purely cosmetic. The compiler ignores them entirely. They exist so you can collapse a section of a long file in your editor.

Many teams avoid regions. The argument is that if a class is long enough that you need to fold parts of it just to read it, the class is probably doing too much and should be split. Regions can also hide complexity rather than fix it. That said, you'll see them in older codebases and in generated code (the // <auto-generated /> files at the top of WinForms or EF Core scaffolding often use them). Knowing what they are is enough; you do not need to reach for them in new code.

Summary

  • C# has three kinds of comments: // for single-line, /* */ for multi-line, and /// for XML documentation. The first two are stripped during compilation. The third is structured data the compiler can emit.
  • /* */ does not nest. The first */ always closes the block, even if more /* markers appear inside. Use // on each line for larger commented blocks.
  • XML doc comments attach to the member directly below them. Common tags include <summary>, <remarks>, <param>, <returns>, <exception cref="...">, <example>, <code>, <c>, <see cref="..."/>, <seealso cref="..."/>, and <typeparam name="T">.
  • Set <GenerateDocumentationFile>true</GenerateDocumentationFile> in the .csproj to emit a .xml file next to the compiled .dll. IDEs read it for IntelliSense, and tools like DocFX turn it into doc sites.
  • Enabling doc generation also turns on warning CS1591 for undocumented public members. Treat it as a guide to which parts of your API still need docs.
  • Good comments explain WHY, not WHAT. Keep them updated, avoid commenting obvious code, and delete commented-out code instead of leaving it behind.
  • #region / #endregion create foldable IDE blocks. They are purely cosmetic and most modern C# codebases avoid them in new code.

The _Naming Conventions_ lesson covers the casing rules C# uses for types, methods, properties, fields, parameters, and locals, and why following them makes your code blend in with the .NET ecosystem.