tags:

views:

807

answers:

6

My application deals with percentages a lot. These are generally stored in the database in their written form rather than decimal form (50% would be stored as 50 rather than 0.5). There is also the requirement that percentages are formatted consistently throughout the application.

To this end i have been considering creating a struct called percentage that encapsulates this behaviour. I guess its signature would look something like this:

public struct Percentage
{
    public static Percentage FromWrittenValue();
    public static Percentage FromDecimalValue();

    public decimal WrittenValue { get; set; }
    public decimal DecimalValue { get; set; }
}

Is this a reasonable thing to do? It would certianly encapsulate some logic that is repeated many times but it is straightforward logic that peopel are likely to understand. I guess i need to make this type behave like a normal number as much as possible however i am wary of creating implicit conversions to a from decimal in case these confuse people further.

Any suggestions of how to implement this class? or compelling reasons not to.

+3  A: 

Percentage class should not be concerned with formatting itself for the UI. Rather, implement IFormatProvider and ICustomFormatter to handle formatting logic.

As for conversion, I'd go with standard TypeConverter route, which would allow .NET to handle this class correctly, plus a separate PercentageParser utility class, which would delegate calls to TypeDescriptor to be more usable in external code. In addition, you can provide implicit or explicit conversion operator, if this is required.

And when it comes to Percentage, I don't see any compelling reason to wrap simple decimal into a separate struct other than for semantic expressiveness.

Anton Gogolev
I appreciate that the logic should be provided by other classes. But to my mind there is still a lot of value in having a percentage class that uses a default ToString method that gives a nice looking percentage string. Especially as this needs to be displayed in multiple presentation layers. (A mixture of Asp.Net, Winforms, and pdf reports).
Jack Ryan
ToString(), as it is done in other .NET classes, can delegate all the work to the IFormatProvider and ICustomFormatter. See DateTime.ToString for an example.
Anton Gogolev
+1  A: 

I think you may be mixing up presentation and logic here. I would convert the percentage to a decimal or float fraction (0.5) when getting it from the database and then let the presentation deal with the formatting.

Jonas Elfström
+1  A: 

I'd not create a separate class for that - this just creates more overhead. I thinkg it will be faster just to use double variables set to the database value.

If it is common knowledge that the database stores percentages as 50 instead of 0.5, everybody will understand statemens like part = (percentage / 100.0) * (double)value.

Thorsten Dittmar
+2  A: 

I strongly recommend you just stick with using the double type here (I don't see any use for the decimal type either, as wouldn't actually seem to require base-10 precision in the low decimal places). By creating a Percentage type here, you're really performing unnecessary encapsulation and just making it harder to work with the values in code. If you use a double, which is customary for storying percentages (among many other tasks), you'll find dealing with the BCL and other code a lot nicer in most cases.

The only extra functionality that I can see you need for percentages is the ability to convert to/from a percentage string easily. This can be done very simply anyway using single lines of code, or even extension methods if you want to abstract it slightly.

Converting to percentage string :

public static string ToPercentageString(this double value)
{
    return value.ToString("#0.0%"); // e.g. 76.2%
}

Converting from percentage string :

public static double FromPercentageString(this string value)
{
    return double.Parse(value.SubString(0, value.Length - 1)) / 100;
}
Noldorin
If it is the case of needing to convert from strings easily then writing extension methods could be useful.
RichardOD
@RichardOD: You read my mind. I had just been editing the post to give examples of extension methods.
Noldorin
A: 

This question reminds me of the Money class Patterns of Enterprise Application Architecture talks about- the link might give you food for thought.

RichardOD
+1  A: 

It seems like a reasonable thing to do, but I'd reconsider your interface to make it more like other CLR primitive types, e.g. something like.

// all error checking omitted here; you would want range checks etc.
public struct Percentage
{
    public Percentage(decimal value) : this()
    {
        this.Value = value
    }

    public decimal Value { get; private set; }

    public static explicit operator Percentage(decimal d)
    {
        return new Percentage(d);
    }

    public static implicit operator decimal(Percentage p)
    {
        return this.Value;
    }

    public static Percentage Parse(string value)
    {
        return new Percentage(decimal.Parse(value));
    }

    public override string ToString()
    {
        return string.Format("{0}%", this.Value);
    }
}

You'd definitely also want to implement IComparable<T> and IEquatable<T> as well as all the corresponding operators and overrides of Equals, GetHashCode, etc. You'd also probably also want to consider implementing the IConvertible and IFormattable interfaces.

This is a lot of work. The struct is likely to be somewhere in the region of 1000 lines and take a couple of days to do (I know this because it's a similar task to a Money struct I wrote a few months back). If this is of cost-benefit to you, then go for it.

Greg Beech
If he's insistent on taking this approach, it's also worth overloading all the arithmetic and comparison operators.
Noldorin
@Noldorin - Um, I said "as well as all the corresponding operators and overrides of Equals, GetHashCode, etc" in the context of those interfaces...?
Greg Beech