简体   繁体   English

c#创建自定义“双”类型

[英]c# creating a custom “double” type

In my application I want all my properties storing money amounts to be rounded to n decimal places. 在我的应用程序中,我希望所有存储金额的属性都舍入到n位小数。

For code clarity, I'd rather have a custom type MoneyAmount which all my corresponding fields would have, instead of having to put a `Math.Round(value, n)' in all the property getters/setters. 为了清楚代码,我宁愿拥有一个自定义类型MoneyAmount ,它可以包含所有相应字段,而不必在所有属性getter / setter中放置一个`Math.Round(value,n)'。

Is there a neat way to achieve this? 有没有一种巧妙的方法来实现这一目标?

I saw this post about overloading assignment operators - is this the suggested approach? 我看到这篇关于重载赋值运算符的帖子 - 这是建议的方法吗?

EDIT: Given the multiple views, I post the full code I derived here: 编辑:鉴于多个视图,我发布了我在此处派生的完整代码:

public struct MoneyAmount {
const int N = 4;
private readonly double _value;

public MoneyAmount(double value) {
  _value = Math.Round(value, N);
}

#region mathematical operators
public static MoneyAmount operator +(MoneyAmount d1, MoneyAmount d2) {
  return new MoneyAmount(d1._value + d2._value);
}

public static MoneyAmount operator -(MoneyAmount d1, MoneyAmount d2) {
  return new MoneyAmount(d1._value - d2._value);
}

public static MoneyAmount operator *(MoneyAmount d1, MoneyAmount d2) {
  return new MoneyAmount(d1._value * d2._value);
}

public static MoneyAmount operator /(MoneyAmount d1, MoneyAmount d2) {
  return new MoneyAmount(d1._value / d2._value);
}
#endregion

#region logical operators
public static bool operator ==(MoneyAmount d1, MoneyAmount d2) {
  return d1._value == d2._value;
}
public static bool operator !=(MoneyAmount d1, MoneyAmount d2) {
  return d1._value != d2._value;
}
public static bool operator >(MoneyAmount d1, MoneyAmount d2) {
  return d1._value > d2._value;
}
public static bool operator >=(MoneyAmount d1, MoneyAmount d2) {
  return d1._value >= d2._value;
}
public static bool operator <(MoneyAmount d1, MoneyAmount d2) {
  return d1._value < d2._value;
}
public static bool operator <=(MoneyAmount d1, MoneyAmount d2) {
  return d1._value <= d2._value;
}
#endregion

#region Implicit conversions
/// <summary>
/// Implicit conversion from int to MoneyAmount. 
/// Implicit: No cast operator is required.
/// </summary>
public static implicit operator MoneyAmount(int value) {
  return new MoneyAmount(value);
}

/// <summary>
/// Implicit conversion from float to MoneyAmount. 
/// Implicit: No cast operator is required.
/// </summary>
public static implicit operator MoneyAmount(float value) {
  return new MoneyAmount(value);
}

/// <summary>
/// Implicit conversion from double to MoneyAmount. 
/// Implicit: No cast operator is required.
/// </summary>
public static implicit operator MoneyAmount(double value) {
  return new MoneyAmount(value);
}

/// <summary>
/// Implicit conversion from decimal to MoneyAmount. 
/// Implicit: No cast operator is required.
/// </summary>
public static implicit operator MoneyAmount(decimal value) {
  return new MoneyAmount(Convert.ToDouble(value));
}
#endregion

#region Explicit conversions
/// <summary>
/// Explicit conversion from MoneyAmount to int. 
/// Explicit: A cast operator is required.
/// </summary>
public static explicit operator int(MoneyAmount value) {
  return (int)value._value;
}

/// <summary>
/// Explicit conversion from MoneyAmount to float. 
/// Explicit: A cast operator is required.
/// </summary>
public static explicit operator float(MoneyAmount value) {
  return (float)value._value;
}

/// <summary>
/// Explicit conversion from MoneyAmount to double. 
/// Explicit: A cast operator is required.
/// </summary>
public static explicit operator double(MoneyAmount value) {
  return (double)value._value;
}

/// <summary>
/// Explicit conversion from MoneyAmount to decimal. 
/// Explicit: A cast operator is required.
/// </summary>
public static explicit operator decimal(MoneyAmount value) {
  return Convert.ToDecimal(value._value);
}
#endregion
}

I'd suggest the following: 我建议如下:

  1. Create a new struct, called MoneyAmount. 创建一个名为MoneyAmount的新结构。
  2. It contains one field: A double . 它包含一个字段: double
  3. The constructor with one double parameter, this constructor rounds the value and assigns it to the internal field. 具有一个double参数的构造函数,此构造函数对值进行舍入并将其分配给内部字段。
  4. Add the members/operators you might need to your struct so it has all the same operations as the double , like +, -, etc. But also casts/conversions from/to other types. 将您可能需要的成员/运算符添加到结构中,使其具有与double相同的操作,如+, - 等。还可以从/向其他类型转换/转换。 Every operation produces a new instance of MoneyAmount with a rounded value. 每个操作都会生成一个带有舍入值的MoneyAmount新实例。
  5. Also consider implementing the interfaces IFormattable , IComparable and IConvertible . 还要考虑实现IFormattableIComparableIConvertible接口。

Short example: 简短的例子:

public struct MoneyAmount
{
    const int N = 4;
    private readonly double _value;

    public MoneyAmount(double value)
    {
        _value = Math.Round(value, N);
    }

    // Example of one member of double:
    public static MoneyAmount operator *(MoneyAmount d1, MoneyAmount d2) 
    {
        return new MoneyAmount(d1._value * d2._value);
    }

    /// <summary>
    /// Implicit conversion from double to MoneyAmount. 
    /// Implicit: No cast operator is required.
    /// </summary>
    public static implicit operator MoneyAmount(double value)
    {
        return new MoneyAmount(value);
    }

    /// <summary>
    /// Explicit conversion from MoneyAmount to double. 
    /// Explicit: A cast operator is required.
    /// </summary>
    public static explicit operator double(MoneyAmount value)
    {
        return value._value;
    }

    /// <summary>
    /// Explicit conversion from MoneyAmount to int. 
    /// Explicit: A cast operator is required.
    /// </summary>
    public static explicit operator MoneyAmount(int value)
    {
        return new MoneyAmount(value);
    }

    /// <summary>
    /// Explicit conversion from MoneyAmount to int. 
    /// Explicit: A cast operator is required.
    /// </summary>
    public static explicit operator int(MoneyAmount value)
    {
        return (int)value._value;
    }

    // All other members here...
}

I realize: The double has a lot of members... 我意识到: double有很多成员......

With these operators, the following code is possible: 使用这些运算符,可以使用以下代码:

MoneyAmount m = 1.50; // Assignment from a double.
MoneyAmount n = 10; // Assignment from an integer.
m += n; // Mathematical operation with another MoneyAmount .
m *= 10; // Mathematical operation with an integer.
m -= 12.50; // Mathematical operation with a double.

EDIT 编辑

All conversion methods you may want to implement: 您可能想要实现的所有转换方法:

  • Explicit MoneyAmount --> int 显式MoneyAmount - > int
  • Explicit MoneyAmount --> float 显式MoneyAmount - > float
  • Explicit MoneyAmount --> double 显式MoneyAmount - > double
  • Explicit MoneyAmount --> decimal 显式MoneyAmount - >十进制

  • Implicit int--> MoneyAmount 隐式int - > MoneyAmount

  • Implicit float --> MoneyAmount 隐式浮点数 - > MoneyAmount
  • Implicit double--> MoneyAmount 隐式双 - > MoneyAmount
  • Implicit decimal --> MoneyAmount 隐式小数 - > MoneyAmount

All mathematical operations you may want to implement: 您可能想要实现的所有数学运算:

  • MoneyAmount + MoneyAmount MoneyAmount + MoneyAmount
  • MoneyAmount - MoneyAmount MoneyAmount - MoneyAmount
  • MoneyAmount * MoneyAmount MoneyAmount * MoneyAmount
  • MoneyAmount / MoneyAmount MoneyAmount / MoneyAmount

All relational operations you may want to implement: 您可能想要实现的所有关系操作:

  • MoneyAmount == MoneyAmount MoneyAmount == MoneyAmount
  • MoneyAmount != MoneyAmount MoneyAmount!= MoneyAmount
  • MoneyAmount > MoneyAmount MoneyAmount> MoneyAmount
  • MoneyAmount >= MoneyAmount MoneyAmount> = MoneyAmount
  • MoneyAmount < MoneyAmount MoneyAmount <MoneyAmount
  • MoneyAmount <= MoneyAmount MoneyAmount <= MoneyAmount

With all these operations your have all basics covered. 通过所有这些操作,您可以了解所有基础知识。

This gets big very quickly. 这很快变大了。 Writing a struct is easy, as demonstrated in @MartinMulder's answer, but consider that you will want to overload a number of combinations of operators, as well as including a few implicit/explicit casts as well. 编写结构很容易,如@ MartinMulder的回答所示,但考虑到你需要重载多个运算符组合,以及包括一些隐式/显式转换。

Mathematical & Logical Operation 数学和逻辑运算

Consider that you may want to do mathematical operations on MoneyAmount 考虑您可能想要在MoneyAmount上进行数学运算

  • MoneyAmount + MoneyAmount MoneyAmount + MoneyAmount
  • MoneyAmount + double MoneyAmount + double
  • MoneyAmount + int MoneyAmount + int
  • MoneyAmount + decimal MoneyAmount + decimal

That is 4 overloads of the + operator. 这是+运算符的4次重载。 Rinse and repeat for - , / , * (and possibly % ). 冲洗并重复-/* (可能为% )。 You'll also want to overload < , <= , == and > , >= . 您还需要重载<<===>>= Thats something like 30 operator overloads. 这就像30个运算符重载。 Phew! 唷! Thats a lot of static methods. 这是很多静态方法。

public static MoneyAmount operator +(MoneyAmount d1, double d2) 
{
    return new MoneyAmount((decimal)(d1._value + d2));
}

Explicit/Implicit casts 显式/隐式演员

Now consider that instead of this code 现在考虑代替这个代码

MoneyAmount m = new MoneyAmount(1.234);

You wanted to do this: 你想这样做:

MoneyAmount m = 1.234;

That can be achieved with an implicit cast operator. 这可以通过隐式转换运算符来实现。

public static implicit operator MoneyAmount(double d)
{
    return new MoneyAmount((decimal)d);
}

(You'll need one for every type you want to allow implicit casts) (你想要允许隐式转换的每种类型都需要一个)

Another one: 另一个:

int i = 4;
MoneyAmount m = (MoneyAmount)i;

This is done with an explicit cast operator overload. 这是通过显式转换运算符重载完成的。

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

(Again, 1 for every type you want to allow explicit casts) (同样,对于您希望允许显式转换的每种类型都为1)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM