我在数学库中工作,由于使用double
的固有麻烦,我将所有相等比较类型a == b
编码为Math.Abs(a - b) <= epsilon
。
此外,默认情况下,我希望以最大考虑精度生成格式化的字符串。也就是说,如果 epsilon 为0.001
,我希望我的默认格式为N3
。
很高兴我做了以下事情:
public static class Math3D
{
internal const int ROUND = 3;
public const double Epsilon = 1e-ROUND;
}
...我收到了编译错误。显然这是不允许的。
有了这个限制,我发现我无法将两个相互依赖的常量定义为consts。显然我可以将Epsilon
定义为只读字段,但我觉得这样做在概念上是错误的。我错过了一种明显的方法吗?
答案 0 :(得分:10)
如果您可能正在更改它,那么应该在此使用readonly
。 const
应该用于永远不会更改的内容,例如π。造成这种情况的原因是const
和readonly
之间存在细微差别。
主要问题是,如果更改const
的值,则必须重新编译使用const
的所有依赖客户端,否则你可以shoot yourself in the foot, badly。因此,对于可能更改的值,请勿使用const
,请使用readonly
。
因此,如果永远的值不会改变,只需使用const
,然后不用担心根据Epsilon
定义ROUND
,只是说:
internal const int ROUND = 3;
public const double Epsilon = 1e-3;
如果你真的想确保在不改变另一个的情况下不小心改变一个,你可以在构造函数中添加一个小支票:
if (Epsilon != Math.Pow(10, -ROUND)) {
throw new YouForgotToChangeBothConstVariablesException();
}
您甚至可以添加条件编译,以便仅在调试版本中进行编译。
如果 要更改,请使用static readonly
:
internal readonly int ROUND = 3;
public static readonly double Epsilon = Math.Pow(10, -ROUND);
有了这个限制,我发现我无法将两个相互依赖的常量定义为consts。 [...]我错过了一个明显的方法来做到这一点吗?
不,您需要使用Math.Pow
或Math.Log
进行某种数学运算,以便在ROUND
和Epsilon
之间进行,并且这些数学运算不适用于编译时使用const
。您可以编写一个微型代码生成器,根据单个输入值吐出这两行代码,但我真的质疑投入时间的价值。
答案 1 :(得分:2)
1e-ROUND
,特别是1e
不是有效的文字整数。你必须做类似的事情,
public static readonly double Epsilon =
decimal.Parse(
string.Format("1E-{0}", ROUND),
System.Globalization.NumberStyles.Float);
另外,请注意static readonly
,因为在运行时之前无法知道表达式时无法使用const
。在这种情况下,static readonly
的工作方式与const
类似。
如果您不想处理string
,可以随时使用
public static readonly double Epsilon = Math.Pow(10, -ROUND);
答案 2 :(得分:1)
你总是可以硬编码3.看到你正在使用常量,那么无意将值改为3以外的任何东西吗?所以你不必过分担心DRY。
public static class Math3D
{
internal const int ROUND = 3;
public const double Epsilon = 1e-3;
}
如果您认为自己可能想要更改3,那么const
不适合您,您的问题就没有用了。
答案 3 :(得分:1)
修改强>
这不是您问题的直接答案,但您是否考虑过将Round
和Epsilon
更改为可写字段?如果您使用这些进行格式化/舍入,几乎可以保证他们有时需要更改 - const
和readonly
字段都不适用。
public static class Math3D
{
internal static int s_Round;
internal static double s_Epsilon;
static Math3D ()
{
Round = 3;
}
public static double Epsilon
{
get
{
return ( s_Epsilon );
}
}
public static int Round
{
get
{
return ( s_Round );
}
set
{
// TODO validate
s_Round = value;
s_Epsilon = Math.Pow ( 10, -s_Round );
}
}
}
这是一个清晰可读的解决方案,在您将来改变时不会破坏。