我的应用程序处理了很多百分比。这些通常以书面形式而不是十进制形式存储在数据库中(50%将存储为50而不是0.5)。还要求在整个应用程序中一致地格式化百分比。
为此,我一直在考虑创建一个名为percentage的结构来封装这种行为。我想它的签名看起来像这样:
public struct Percentage
{
public static Percentage FromWrittenValue();
public static Percentage FromDecimalValue();
public decimal WrittenValue { get; set; }
public decimal DecimalValue { get; set; }
}
这是否合理?它会认真地封装一些重复多次的逻辑,但这是人们可能理解的简单逻辑。我想我需要尽可能地使这种类型的行为像普通数字一样,但是我担心从小数创建隐式转换,以防这些人进一步混淆。
有关如何实现此类的任何建议?或令人信服的理由不这样做。
答案 0 :(得分:11)
我实际上对这里对数据质量的傲慢态度有点惊讶。 不幸的是,口语术语“百分比”可以表示两种不同的事物之一:概率和方差。 OP没有指定哪个,但由于通常计算方差,我猜他可能将百分比表示为概率或分数(例如折扣)。
为此目的编写Percentage
类的非常好的理由与演示文稿无关,但确保您可以防止那些愚蠢的愚蠢用户执行输入无效的操作像-5和250这样的值。
我正在考虑一个Probability
类:一个有效范围严格为[0,1]的数字类型。您可以将该规则封装在一个地方,而不是在37个地方编写这样的代码:
public double VeryImportantLibraryMethodNumber37(double consumerProvidedGarbage)
{
if (consumerProvidedGarbage < 0 || consumerProvidedGarbage > 1)
throw new ArgumentOutOfRangeException("Here we go again.");
return someOtherNumber * consumerProvidedGarbage;
}
相反,你有这个很好的实现。不,这并不是非常明显的改进,但请记住,每次使用此值时,您都在进行价值检查。
public double VeryImportantLibraryMethodNumber37(Percentage guaranteedCleanData)
{
return someOtherNumber * guaranteedCleanData.Value;
}
答案 1 :(得分:5)
Percentage
类不应该关注UI的格式化本身。相反,实现IFormatProvider和ICustomFormatter来处理格式化逻辑。
至于转换,我会使用标准TypeConverter路由,这将允许.NET正确处理此类,以及一个单独的PercentageParser
实用程序类,它将调用委托给{{1在外部代码中更有用。此外,如果需要,您可以提供TypeDescriptor
或implicit
转换运算符。
当谈到explicit
时,除了语义表达之外,我没有看到任何令人信服的理由将简单的Percentage
包装成单独的decimal
。
答案 2 :(得分:4)
我强烈建议您坚持使用此处的double
类型(我看不到decimal
类型的任何用途,因为实际上似乎不需要基数为10的精度低位小数)。通过在此处创建Percentage
类型,您实际上正在执行不必要的封装,并且只是更难以使用代码中的值。如果你使用double
,这是故事百分比(在许多其他任务中)的习惯,你会发现在大多数情况下处理BCL和其他代码要好得多。
我可以看到百分比所需的唯一额外功能是能够轻松转换为/从百分比字符串转换。无论如何,这可以使用单行代码完成,如果你想稍微抽象一下,甚至可以使用扩展方法。
转换为百分比字符串:
public static string ToPercentageString(this double value)
{
return value.ToString("#0.0%"); // e.g. 76.2%
}
从百分比字符串转换:
public static double FromPercentageString(this string value)
{
return double.Parse(value.SubString(0, value.Length - 1)) / 100;
}
答案 3 :(得分:3)
这似乎是一件合理的事情,但我会重新考虑你的界面,使其更像其他CLR基元类型,例如:类似的东西。
// 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);
}
}
您当然也希望实施IComparable<T>
和IEquatable<T>
以及Equals
,GetHashCode
等所有相应的运算符和覆盖。您还需要可能还想考虑实现IConvertible
和IFormattable
接口。
这是很多工作。结构很可能在1000行的某个地方,需要花费几天时间(我知道这是因为它与我几个月前写的Money
结构类似)。如果这对您有成本效益,那就去吧。
答案 4 :(得分:2)
这个问题让我想起了金钱课Patterns of Enterprise Application Architecture所说的 - 这个链接可能会让你思考。
答案 5 :(得分:1)
我认为你可能会在这里混淆演示和逻辑。当从数据库中获取它时,我会将百分比转换为十进制或浮点数(0.5),然后让演示文稿处理格式。
答案 6 :(得分:1)
我不会为此创建一个单独的类 - 这只会产生更多的开销。我认为将double
变量设置为数据库值会更快。
如果众所周知数据库将百分比存储为50而不是0.5,那么每个人都会理解像part = (percentage / 100.0) * (double)value
这样的状态。