GENERIC C# 将数字四舍五入到最接近的倍数的方法? (不是重复的,有特定的角度和建议的答案)

GENERIC C# way to round a number to a nearest multiple? (Not a duplicate, has a specific angle & proposed answer)

我有一个C#扩展方法如下:

public static double RoundOff(this double rawNumber, double roundToNearest)
{
    double rawMultiples     = rawNumber / roundToNearest;
    double roundedMultiples = Math.Round(rawMultiples);
    double roundedNumber    = roundedMultiples * roundToNearest;
    return roundedNumber;
}

我不想为所有不同的数字类型(int、decimal 等)多次编写它

有没有办法像这样通用地做到这一点?

public static double RoundOff<T>(this T rawNumber, T roundToNearest)
    where T : [SOME CONSTRAINT]
{
    T rawMultiples     = rawNumber / roundToNearest;
    T roundedMultiples = Math.Round(rawMultiples);
    T roundedNumber    = roundedMultiples * roundToNearest;
    return roundedNumber;
}

如果能够更普遍地执行此操作将非常有用。需要维护的代码更少 - 只需一种扩展方法即可获得更多功能!

如果做不到,是因为C#不能扩展到这样工作吗?或者有一天它会被扩展以允许 "all numeric types" 通用约束?

欢迎任何想法。

更新 回应关于与另一个问题的相似性的挑战。是的,它在主题方面是相似的,但又有所不同,因为我正在寻找针对特定问题的特定解决方案。我在下面添加了我自己的尝试答案以进行澄清。如果还有人认为我没说到重点,欢迎挑战。

显然不可能像其他评论员所说的那样用泛型做你想做的事情,但由于所有数字都可以转换为双精度,你至少可以减少逻辑重复:

public static double RoundOff(this long rawNumber, double roundToNearest)
{
    return RoundOff((double) rawNumber, roundToNearest);
}

public static double RoundOff(this double rawNumber, double roundToNearest)
{
    T rawMultiples     = rawNumber / roundToNearest;
    T roundedMultiples = Math.Round(rawMultiples);
    T roundedNumber    = roundedMultiples * roundToNearest;
    return roundedNumber;
}

如果您不使用扩展方法进行管理,那么您可以直接对任何数字调用此方法,因为 C# 会自动将所有类型转换为双精度

int i = 5;
RoundOff(i, 0.5);

我承认这个问题与其他问题有一些相似之处 - 但我的查询是关于解决一个非常具体的问题的非常具体的问题。

我想在这里分享我自己的解决方案。从好的方面来说,它是完全通用的。不利的一面是,它使用 Decimals 时速度较慢,但​​如果需要,可以将其调整为更快的 double。

    public static T RoundOff<T>(this T rawNumber, T roundToNearest)
        where T : IComparable<T>
    {
        if (typeof(T).IsNumericType())
        {
            decimal decimalRoundToNearest   = Convert.ToDecimal(roundToNearest);
            decimal rawMultiples            = Convert.ToDecimal(rawNumber) / Convert.ToDecimal(roundToNearest);
            decimal decimalRoundedMultiples = Math.Round(rawMultiples);
            decimal decimalRoundedNumber    = decimalRoundedMultiples * decimalRoundToNearest;

            return (T)Convert.ChangeType(decimalRoundedNumber, typeof(T));
            // alternative
            // TypeConverter converter = TypeDescriptor.GetConverter(typeof(T));
            // return (T)converter.ConvertFrom(decimalRoundedNumber);

        }
        else
        {
            throw new Exception("Type " + typeof(T) + " is not numeric");
        }
    }

并且这种扩展方法的逻辑借鉴了引用的答案

    public static bool IsNumericType(this object o)
    {
        // 
        switch (Type.GetTypeCode(o.GetType()))
        {
            case TypeCode.Byte:
            case TypeCode.SByte:
            case TypeCode.UInt16:
            case TypeCode.UInt32:
            case TypeCode.UInt64:
            case TypeCode.Int16:
            case TypeCode.Int32:
            case TypeCode.Int64:
            case TypeCode.Decimal:
            case TypeCode.Double:
            case TypeCode.Single:
                return true;

            default:
                return false;
        }
    }

它并非没有弱点,但它确实满足了我的需求。

一个自我批评:因为我们正在四舍五入,所以小数的额外精度可能是不必要的 - 但直觉上使用高精度类型似乎更好。我在这方面可能是错的,也许更有知识的人可以提供一些见解。

所以我希望这可以帮助某人或激起大量关于如何做得更好的愤怒反应!!