In .Net 7, there is IFloatingPoint
. I read the following code for .Net 6.
It uses struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
. Are all these constraint necessary?
Code:
using System;
namespace MyExtensions
{
public static class NumericExtensions
{
public static T Round<T>(this T value, int decimalPlaces) where T :
struct, IComparable, IFormattable, IConvertible, IComparable<T>, IEquatable<T>
{
decimal decimalValue = Convert.ToDecimal(value);
decimal rounded = Math.Round(decimalValue, decimalPlaces);
return (T)Convert.ChangeType(rounded, typeof(T));
}
}
}
CodePudding user response:
Rounding only makes sense for floating point numbers. In C#, we only have three types of floating point numbers (float
, double
, decimal
). The Math.Round
methods exists in two variants, one for double
and one for decimal
. Both use different rounding algorithms internally. Using the rounding method for doubles on decimals or vice versa can lead to unexpected results.
Since we only have three relevant types, the simplest approach for generating extension methods is to write one for each type. One line is enough per type:
public static class NumericExtensions
{
public static double Round(this double value, int decimalPlaces) => Math.Round(value, decimalPlaces);
public static decimal Round(this decimal value, int decimalPlaces) => Math.Round(value, decimalPlaces);
public static float Round(this float value, int decimalPlaces) => (float)Math.Round(value, decimalPlaces);
}
Starting with .NET 7, the numeric types in the framework were extended to implement generic interfaces that make it easier to write generic methods that deal with numbers. There is now also Round()
methods on the floating point types themselves, so that we don't have to use Math.Round()
anymore but can use the Round()
method from the actual type.
With this new support, we can write a single extension method that supports all floating point types (also including the new Half
type):
public static class NumericExtensions
{
public static T Round<T>(this T value, int decimalPlaces) where T : IFloatingPoint<T> => T.Round(value, decimalPlaces);
}