When your command needs to accept complex types that aren't built-in—like points, colors, or domain-specific values—create a custom TypeConverter to parse the string input into your type.
What We're Building
A drawing command accepting Point values in X,Y format. The converter parses --point 10,20 into a strongly-typed struct:
Create a Type Converter
Inherit from System.ComponentModel.TypeConverter and override CanConvertFrom and ConvertFrom:
public sealed class PointConverter : TypeConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
=> sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object value)
{
if (value is string str)
{
var parts = str.Split(',');
if (parts.Length == 2 &&
int.TryParse(parts[0].Trim(), out var x) &&
int.TryParse(parts[1].Trim(), out var y))
{
return new Point(x, y);
}
throw new FormatException($"Invalid point format: '{str}'. Expected format: X,Y (e.g., 10,20)");
}
return base.ConvertFrom(context, culture, value);
}
}
The Point type it converts to:
public readonly record struct Point(int X, int Y)
{
public override string ToString() => $"({X}, {Y})";
}
Apply the Converter to an Option
Use the [TypeConverter] attribute on your settings property to specify which converter to use:
public class Settings : CommandSettings
{
// Custom type with TypeConverter attribute
[CommandOption("--point <POINT>")]
[Description("A point in X,Y format (e.g., 10,20)")]
[TypeConverter(typeof(PointConverter))]
public required Point Location { get; init; }
// Optional point with nullable
[CommandOption("--offset [OFFSET]")]
[Description("Optional offset point")]
[TypeConverter(typeof(PointConverter))]
public required FlagValue<Point> Offset { get; init; }
[CommandOption("-c|--color")]
[Description("The drawing color")]
[DefaultValue("black")]
public string Color { get; init; } = "black";
}
Users can now pass points on the command line:
myapp --point 10,20 --color red
myapp --point 100,200 --offset 5,5
Handle Parsing Errors
When the input doesn't match your expected format, throw a FormatException with a helpful message. The framework displays this message to the user:
if (parts.Length != 2 ||
!int.TryParse(parts[0].Trim(), out var x) ||
!int.TryParse(parts[1].Trim(), out var y))
{
throw new FormatException(
$"Invalid point format: '{str}'. Expected format: X,Y (e.g., 10,20)");
}
When a user provides invalid input like --point abc:
Error: Invalid point format: 'abc'. Expected format: X,Y (e.g., 10,20)
Register Converters on Types You Own
For types you define, you can apply the converter directly to the type instead of each property:
[TypeConverter(typeof(PointConverter))]
public readonly record struct Point(int X, int Y);
Then any property of type Point automatically uses the converter without needing the attribute.
See Also
- Type Converters - Complete type converter reference
- Defining Commands and Arguments - Basic argument and option patterns