Skip to content

Commit b883370

Browse files
committed
beta5 breaking changes: renaming, the separation of names and aliases, exposing mutable collections, default value changes
1 parent 0a11108 commit b883370

File tree

2 files changed

+144
-0
lines changed

2 files changed

+144
-0
lines changed

docs/standard/commandline/beta5.md

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
---
2+
title: System.CommandLine beta5 breaking changes
3+
description: "Learn what breaking changes were introduced in beta5 and why."
4+
ms.date: 16/06/2025
5+
no-loc: [System.CommandLine]
6+
helpviewer_keywords:
7+
- "command line interface"
8+
- "command line"
9+
- "System.CommandLine"
10+
ms.topic: how-to
11+
---
12+
13+
# Breaking changes in beta5
14+
15+
[!INCLUDE [scl-preview](../../../includes/scl-preview.md)]
16+
17+
Our main focus for beta5 was to improve the API, by simplifying it, making it more consistent and coherent with [Framework design guidelines](../design-guidelines/index.md). This article describes the changes that you need to make to your code to use beta5.
18+
19+
## Renaming
20+
21+
For type names, we wanted to disambiguate from other single-word types to avoid name conflicts. We also renamed some properties to make them follow the [guidelines for property names](../design-guidelines/names-of-type-members.md#names-of-properties). The following table shows the old and new names:
22+
23+
| Old name | New name |
24+
|-------------------------------------------------------------|----------------------------------------------------------------|
25+
| `System.CommandLine.Parsing.Parser` | `System.CommandLine.Parsing.CommandLineParser` |
26+
| `System.CommandLine.Parsing.OptionResult.IsImplicit` | `System.CommandLine.Parsing.OptionResult.Implicit` |
27+
| `System.CommandLine.Option.IsRequired` | `System.CommandLine.Option.Required` |
28+
| `System.CommandLine.Symbol.IsHidden` | `System.CommandLine.Symbol.Hidden` |
29+
| `System.CommandLine.Option.ArgumentHelpName` | `System.CommandLine.Option.HelpName` |
30+
| `System.CommandLine.Parsing.OptionResult.Token` | `System.CommandLine.Parsing.OptionResult.IdentifierToken` |
31+
| `System.CommandLine.Parsing.ParseResult.FindResultFor` | `System.CommandLine.Parsing.ParseResult.GetResult` |
32+
| `System.CommandLine.Parsing.SymbolResult.ErrorMessage` | `System.CommandLine.Parsing.SymbolResult.AddError` |
33+
34+
In case of the `ErrorMessage` property, we changed the name to `AddError` and made it a method. The goal was to allow to report multiple errors for the same symbol, which is useful when you want to report validation errors.
35+
36+
## Exposing mutable collections
37+
38+
In beta5, we changed the API to expose mutable collections instead of `Add` methods and (sometimes) readonly collections. This allows you to not only add items or enumerate them, but also remove them. The following table shows the old method and new property names:
39+
40+
| Old method name | New property |
41+
|-------------------------------------------------------------|----------------------------------------------------------------|
42+
| `System.CommandLine.Command.AddArgument` | `System.CommandLine.Command.Arguments` |
43+
| `System.CommandLine.Command.AddOption` | `System.CommandLine.Command.Options` |
44+
| `System.CommandLine.Command.AddCommand` | `System.CommandLine.Command.Subcommands` |
45+
| `System.CommandLine.Command.AddValidator` | `System.CommandLine.Command.Validators` |
46+
| `System.CommandLine.Option.AddValidator` | `System.CommandLine.Option.Validators` |
47+
| `System.CommandLine.Argument.AddValidator` | `System.CommandLine.Argument.Validators` |
48+
| `System.CommandLine.Command.AddCompletions` | `System.CommandLine.Command.CompletionSources` |
49+
| `System.CommandLine.Option.AddCompletions` | `System.CommandLine.Option.CompletionSources` |
50+
| `System.CommandLine.Argument.AddCompletions` | `System.CommandLine.Argument.CompletionSources` |
51+
| `System.CommandLine.Command.AddAlias` | `System.CommandLine.Command.Aliases` |
52+
| `System.CommandLine.Option.AddAlias` | `System.CommandLine.Option.Aliases` |
53+
54+
## Names and aliases
55+
56+
Before beta5, the `System.CommandLine.Parsing.IdentifierSymbol` class was used to represent a base class for symbols that could be identified by a name and/or aliases (`Option` and `Command`). In beta5, every `Symbol` must provide a name, and only `Option` and `Command` support aliases. As a result, we made the following changes:
57+
58+
- `name` is now a mandatory argument for every public constructor of `Argument`, `Option`, and `Command`. In case of `Argument`, it is not used for parsing, but to generate the help and completions text. In case of `Option` and `Command`, it is used to identify the symbol during parsing and also for help and completions.
59+
- `Symbol.Name` is no longer `virtual`, it's also readonly now and it returns the name as it was provided when the symbol was created. Because of that, `Symbol.DefaultName` was removed and `Option.Name` no longer removes the `--`, `-` or `/` or any other prefix from the longest alias.
60+
- The `Aliases` property exposed by `Option` and `Command` is now exposing a mutable collection. This collection no longer includes the name of the symbol.
61+
- `System.CommandLine.Parsing.IdentifierSymbol` was removed.
62+
63+
Having the name always present allows for [getting the parsed value by name](parse-and-invoke.md#getvalue):
64+
65+
```csharp
66+
RootCommand command = new("The description.")
67+
{
68+
new Option<int>("--number")
69+
};
70+
71+
ParseResult parseResult = command.Parse(args);
72+
int number = parseResult.GetValue<int>("--number");
73+
```
74+
75+
### Creating options with aliases
76+
77+
We expect that most of the time, you will want to create options with aliases. In beta5, you can do this by using the `Option<T>` constructor that takes a name and `params` array of aliases.
78+
79+
In the past, `Option<T>` had a constructor that took a name and a description. Because of that, the second argument might now be treated as an alias rather than a description. It's the only known breaking change in the API that is not going to cause a compiler error.
80+
81+
Old code that used the constructor with a description should be updated to use the new constructor that takes a name and aliases, and then set the `Description` property separately. For example:
82+
83+
```csharp
84+
Option<bool> beta4 = new("--help", "An option with aliases.");
85+
beta4b.Aliases.Add("-h");
86+
beta4b.Aliases.Add("/h");
87+
88+
Option<bool> beta5 = new("--help", "-h", "/h")
89+
{
90+
Description = "An option with aliases."
91+
};
92+
```
93+
94+
## Default values
95+
96+
In beta4, users could set default values for options and arguments by using the `SetDefaultValue` methods. Those methods were accepting an `object` value, which was not type-safe and could lead to runtime errors if the value was not compatible with the option or argument type:
97+
98+
```csharp
99+
Option<int> option = new("--number");
100+
option.SetDefaultValue("text"); // This is not type-safe, as the value is a string, not an int.
101+
```
102+
103+
Moreover, some of the `Option` and `Argument` constructors accepted a parse delegate and a boolean indicating whether the delegate was a custom parser or a default value provider. This was confusing.
104+
105+
`Option<T>` and `Argument<T>` classes now have a `DefaultValueFactory` property that can be used to set the default value for the symbol. This property is invoked when the symbol is not provided in the command line input.
106+
107+
```csharp
108+
Option<int> number = new("--number")
109+
{
110+
DefaultValueFactory = _ => 42
111+
};
112+
```
113+
114+
`Argument<T>` and `Option<T>` also come with a `CustomParser` property that can be used to set a custom parser for the symbol:
115+
116+
```csharp
117+
Argument<Uri> uri = new("arg")
118+
{
119+
CustomParser = result =>
120+
{
121+
if (!Uri.TryCreate(result.Tokens.Single().Value, UriKind.RelativeOrAbsolute, out var uriValue))
122+
{
123+
result.AddError("Invalid URI format.");
124+
return null;
125+
}
126+
127+
return uriValue;
128+
}
129+
};
130+
```
131+
132+
Moreover, `CustomParser` accepts `Func<ParseResult, T>` delegate, rather than dedicated `ParseArgument` delegate (it got removed).
133+
134+
For more examples of how to use `DefaultValueFactory` and `CustomParser`, see the [How to customize parsing and validation in System.CommandLine](parsing-and-validation.md) document.
135+
136+
## The benefits of the simplified API
137+
138+
PERF
139+
AOT
140+
141+
## See also
142+
143+
- [System.CommandLine overview](index.md)

docs/standard/commandline/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ To learn more, see the following resources:
5151
- [How to enable and customize tab completion](tab-completion.md)
5252
- [Command-line design guidance](design-guidance.md)
5353
- [System.CommandLine API reference](xref:System.CommandLine)
54+
- [How to update to beta5](beta5.md)

0 commit comments

Comments
 (0)