HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Custom string formatters

Submitted by: @import:stackexchange-codereview··
0
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 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 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.