代码之家  ›  专栏  ›  技术社区  ›  Jack Ryan

在C中创建百分比类型#

c#
  •  7
  • Jack Ryan  · 技术社区  · 15 年前

    我的应用程序处理的百分比很多。它们通常以书面形式而不是十进制形式存储在数据库中(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; }
    }
    

    这是合理的做法吗?它确实封装了一些重复多次的逻辑,但这是人们可能理解的直接逻辑。我想我需要使这种类型的行为尽可能像一个正常的数字,但是我要小心创建隐式转换为从十进制,以防这些进一步混淆人们。

    关于如何实现这个类有什么建议吗?或者令人信服的理由。

    7 回复  |  直到 15 年前
        1
  •  5
  •   Anton Gogolev    15 年前

    Percentage 类不应该关心为UI设置自身的格式。相反,实施 IFormatProvider ICustomFormatter 处理格式化逻辑。

    至于转换,我会用标准的 TypeConverter 路由,它将允许.NET正确处理此类,并且 PercentageParser 实用程序类,将调用委托给 TypeDescriptor 在外部代码中更有用。此外,您还可以提供 implicit explicit 转换运算符(如果需要)。

    当涉及到 百分比 我看不出任何令人信服的理由来简单包装 decimal 分成一个 struct 除了语义表达。

        2
  •  10
  •   Michael Blackburn    13 年前

    我对这里数据质量的傲慢态度有点吃惊。 不幸的是,口语中的“百分比”可以指两种不同的事物之一:概率和方差。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;
     }
    
        3
  •  4
  •   Noldorin    15 年前

    我强烈建议您坚持使用 double 在这里键入(我看不到 decimal 键入任意一个,因为实际上似乎不需要在小数位数较低的地方输入以10为基数的精度)。通过创建 Percentage 在这里输入,您实际上执行了不必要的封装,只是让代码中的值更难处理。如果你使用 双重的 这是故事百分比的惯例(在许多其他任务中),在大多数情况下,您会发现处理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;
    }
    
        4
  •  3
  •   Greg Beech    15 年前

    这似乎是一件合理的事情,但我会重新考虑您的接口,使其更像其他的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 我几个月前写的结构图)。如果这对你有成本效益,那就去做吧。

        5
  •  2
  •   RichardOD    15 年前

    这个问题让我想起了金钱课 Patterns of Enterprise Application Architecture 谈论-这个链接可能会让你深思熟虑。

        6
  •  1
  •   Jonas Elfström    15 年前

    我想你可能把陈述和逻辑混在一起了。当从数据库中获取百分比时,我会将其转换为十进制或浮点数(0.5),然后让演示文稿处理格式。

        7
  •  1
  •   Thorsten Dittmar    15 年前

    我不会为此创建单独的类-这只会产生更多的开销。我想只要用它就会更快 double 变量设置为数据库值。

    如果众所周知,数据库将百分比存储为50而不是0.5,那么每个人都会像这样理解状态 part = (percentage / 100.0) * (double)value .