Vogen
Vogen copied to clipboard
IFormatProvider enhancements
Describe the feature
I have multiple temperature types and each has a suffix I'd like to apply via a format provider. Since there isn’t a mechanism to “register” a default format provider for a type globally I'd like the opportunity to provide one. For example:
There are numerous other places I'd like to insert the format provider too if the passed one is null.
More examples in my manually created type:
public sealed class Celcius : IFormattable
{
// Your custom default format provider, for instance as a singleton.
public static readonly IFormatProvider DefaultFormatProvider = new CelsiusFormatProvider();
// IFormattable implementation
public string ToString(string format, IFormatProvider formatProvider)
{
// Use default format provider if none is provided.
formatProvider ??= DefaultFormatProvider;
// If format is null or empty, default to "G"
if (string.IsNullOrEmpty(format))
format = "G";
sample of the default format provider:
/// <summary>
/// Provides formatting for Celsius values, appending the "°C" symbol.
/// </summary>
public class CelsiusFormatProvider : IFormatProvider, ICustomFormatter
{
private readonly IFormatProvider _parentProvider;
private const string CelsiusSymbol = "°C";
public CelsiusFormatProvider() : this(CultureInfo.CurrentCulture)
{
}
public CelsiusFormatProvider(IFormatProvider parentProvider)
{
_parentProvider = parentProvider;
}
/// <summary>
/// Returns an object that provides formatting services for the specified type.
/// </summary>
public object? GetFormat(Type? formatType)
{
return formatType == typeof(ICustomFormatter) ? this : (object?)null;
}
/// <summary>
/// Converts the value of a specified object to an equivalent string representation
/// using specified format and culture-specific formatting information.
/// </summary>
public string Format(string? format, object? arg, IFormatProvider? formatProvider)
{
// Return null to let other formatters handle this if not a Celsius value
if (arg is null or not Celcius)
{
return HandleOtherFormats(format, arg);
}
Celcius celsius = (Celcius)arg;
if (!celsius.IsInitialized())
{
return "[UNINITIALIZED]";
}
string formattedNumber;
// Handle "C" format specifically for Celsius
if (string.Equals(format, "C", StringComparison.OrdinalIgnoreCase))
{
formattedNumber = celsius.Value.ToString("0.##", _parentProvider);
return $"{formattedNumber}{CelsiusSymbol}";
}
// Use specified format (or default if null) for the number portion
formattedNumber = celsius.Value.ToString(format, _parentProvider);
return $"{formattedNumber}{CelsiusSymbol}";
}
private string HandleOtherFormats(string? format, object? arg)
{
return arg is IFormattable formattable ? formattable.ToString(format, _parentProvider) : arg?.ToString() ?? string.Empty;
}
/// <summary>
/// Attempts to parse a string that might contain the Celsius symbol.
/// </summary>
/// <param name="s">The string to parse.</param>
/// <param name="result">The decimal result if parsing succeeds.</param>
/// <returns>True if parsing succeeds, false otherwise.</returns>
public static bool TryParseCelsiusString(string? s, out decimal result)
{
result = 0;
if (string.IsNullOrWhiteSpace(s))
{
return false;
}
// Remove the Celsius symbol if present
string valueToParse = s.Replace(CelsiusSymbol, "").Trim();
return decimal.TryParse(valueToParse, out result);
}
}
Thanks for the feedback! Apologies for the very slow response. I'll hopefully get around to addressing a few issues soon.