patterncsharpMinor
Custom string formatters
Viewed 0 times
customformattersstring
Problem
Some time ago I was experimenting with an Extendable format provider but I wasn't satisfied with the implementation. It didn't feal very SOLID so I've rewritten it to this:
The base class is the
It supports culture info and by default the invariant culture. As we already have to implement the
As an example here are three formatters:
```
public class DecimalColorFormatter : Formatter
{
public override string Format(string format, object arg, IFormatProvider formatProvider)
{
if (!(arg is Color))
{
return null;
}
var color = (Color)arg;
var argb = color.ToArgb();
if (format.Equals("rgb-dec", StringComparison.OrdinalIgnoreCase))
{
return String.Format(Culture, "({0},{1},{2})", color.R, color.G, color.B);
}
if (format.Equals("argb-dec", StringComparison.OrdinalIgnoreCase))
{
return String.Format(Culture, "({0},{1},{2},{3})", color.A, color.R, color.G, color.B);
}
return null;
}
}
public class HexadecimalColorFormatter : Formatter
{
public override string Format(string format, object arg, IFormatProvider formatProvider)
The base class is the
Formatter:public abstract class Formatter : IFormatProvider, ICustomFormatter
{
protected Formatter(CultureInfo culture = null)
{
Culture = culture ?? CultureInfo.InvariantCulture;
}
public static Formatter Default(CultureInfo culture = null) => new CompositeFormatter(culture);
public CultureInfo Culture { get; }
public virtual object GetFormat(Type formatType)
{
// null means use the default .net formatter
return formatType == typeof(ICustomFormatter) ? this : null;
}
public abstract string Format(string format, object arg, IFormatProvider formatProvider);
}It supports culture info and by default the invariant culture. As we already have to implement the
Format method I didn't want to create another one like TryFormat and if the formatting didn't work I return null... but maybe I should have go the TryFormat way?As an example here are three formatters:
```
public class DecimalColorFormatter : Formatter
{
public override string Format(string format, object arg, IFormatProvider formatProvider)
{
if (!(arg is Color))
{
return null;
}
var color = (Color)arg;
var argb = color.ToArgb();
if (format.Equals("rgb-dec", StringComparison.OrdinalIgnoreCase))
{
return String.Format(Culture, "({0},{1},{2})", color.R, color.G, color.B);
}
if (format.Equals("argb-dec", StringComparison.OrdinalIgnoreCase))
{
return String.Format(Culture, "({0},{1},{2},{3})", color.A, color.R, color.G, color.B);
}
return null;
}
}
public class HexadecimalColorFormatter : Formatter
{
public override string Format(string format, object arg, IFormatProvider formatProvider)
Solution
Unused variables
First of all, there are
Best regards :)
First of all, there are
argbvariables in your code which are not used, so lets remove them.
Type keywords
Secondly you use String class specifier instead of string, while the second one is strongly suggested by Microsoft. I would have changed that for your code to be consistent with code of other .NET developers.
Unnecessary constructor
Thirdly, if you use default parameters like this in the first constructor of CompositeFormatter
public CompositeFormatter(CultureInfo culture = null, params Formatter[] formatters) : base(culture)
{
_formatters = formatters;
}
then you have no need for second one and it can be removed.
Creation of not required objects
Fourthly, your Add extension create new instance of CompositeFormatter every time and they are kept in memory which is not a good practice. And the execution travels through a lot of objects because of this.
So what we can do is rewrite Formatter[] _formatters to List.
Then in constructor _formatters = formatters.ToList().
Later we can add next method to CompositeFormatter
public void Add(Formatter formatter)
{
_formatters.Add(formatter);
}
Finally, we can rewrite your extension like this
public static Formatter Add(this Formatter formatter, CultureInfo culture = null)
where T : Formatter, new()
{
var newFormatter = new T();
var compositeFormatter = formatter as CompositeFormatter;
if (compositeFormatter == null)
{
return new CompositeFormatter(culture, formatter, newFormatter);
}
compositeFormatter.Add(newFormatter);
return compositeFormatter;
}
Few final notes.
Your _formatters should be marked as readonly since you are only assigning value to it in the constructor.
Make classes that are not intended for inheritance sealed` not only because it improves the code style and prevents design flaws but also because it improves performance.Best regards :)
Code Snippets
public CompositeFormatter(CultureInfo culture = null, params Formatter[] formatters) : base(culture)
{
_formatters = formatters;
}public void Add(Formatter formatter)
{
_formatters.Add(formatter);
}public static Formatter Add<T>(this Formatter formatter, CultureInfo culture = null)
where T : Formatter, new()
{
var newFormatter = new T();
var compositeFormatter = formatter as CompositeFormatter;
if (compositeFormatter == null)
{
return new CompositeFormatter(culture, formatter, newFormatter);
}
compositeFormatter.Add(newFormatter);
return compositeFormatter;
}Context
StackExchange Code Review Q#138747, answer score: 3
Revisions (0)
No revisions yet.