为什么IsNan是Double类的静态方法而不是实例属性?

问题在标题中,为什么:

return double.IsNaN(0.6d) && double.IsNaN(x); 

代替

 return (0.6d).IsNaN && x.IsNaN; 

我问,因为当实现具有与NaN相同含义的特殊值的自定义结构时,我倾向于选择第二个。

此外,该属性的性能通常更好,因为它避免复制堆栈上的结构以调用IsNaN静态方法(并且因为我的属性不是虚拟的,所以没有自动装箱的风险)。 当然,对于内置类型来说这不是一个真正的问题,因为JIT可以很容易地优化它。

我现在最好的猜测是,因为你不能在double类中同时拥有属性和静态方法,所以他们更喜欢java启发的语法。 (实际上你可以同时定义一个get_IsNaN属性getter,另一个定义一个IsNaN静态方法,但它会混淆任何支持属性语法的.Net语言)

有趣的问题; 不知道答案 – 但如果它确实让你烦恼,你可以声明一个扩展方法,但它仍然会使用堆栈等。

 static bool IsNaN(this double value) { return double.IsNaN(value); } static void Main() { double x = 123.4; bool isNan = x.IsNaN(); } 

如果C#具有扩展属性会更好(对于语法),但上面是关于你现在可以获得的最接近的,但它应该“内联”得相当好。


更新; 想一想,静态和实例之间还有另一个区别; C# 总是使用“ callvirt ”调用实例方法而不是“ call ”,即使其类型被密封为非可空。 那么将它静态化可能会带来性能上的好处吗? 幸运的是,扩展方法仍然算作静态,因此您可以保留此行为。

静态方法是线程安全的,基元上的方法通常需要是线程安全的,以支持平台中的线程(意味着至少安全地不受内部竞争条件限制),实例方法采用托管指向结构的指针,这意味着结构/原语可能是另一方面,静态方法采用结构/原语的副本,因此在线程竞争条件下是安全的。

如果结构是线程安全的,那么只有在进行primefaces操作时才应该将方法设为实例方法,否则应该选择静态方法。

(作为另一种选择,可以使用使用锁定的实例方法,但它们比复制更昂贵)

编辑:@VirtualBlackFox我已经准备好并举例说明结构上的实例方法即使在不可变结构上也不是线程安全的:

 using System; using System.Threading; namespace CA64213434234 { class Program { static void Main(string[] args) { ManualResetEvent ev = new ManualResetEvent(false); Foo bar = new Foo(0); Action a = () => bar.Display(ev); IAsyncResult ar = a.BeginInvoke(null, null); ev.WaitOne(); bar = new Foo(5); ar.AsyncWaitHandle.WaitOne(); } } public struct Foo { private readonly int val; public Foo(int value) { val = value; } public void Display(ManualResetEvent ev) { Console.WriteLine(val); ev.Set(); Thread.Sleep(2000); Console.WriteLine(val); } } } 

显示Instance方法打印:0 5

即使结构是不可改变的。 对于线程安全方法,使用静态方法。

@Pop Catalin:我对你说的话不满意:

如果结构是线程安全的,那么只有在进行primefaces操作时才应该将方法设为实例方法,否则应该选择静态方法。

这是一个小程序,它表明静态方法不能解决结构的这个问题:

 using System; using System.Threading; using System.Diagnostics; namespace ThreadTest { class Program { struct SmallMatrix { double m_a, m_b, m_c, m_d; public SmallMatrix(double x) { m_a = x; m_b = x; m_c = x; m_d = x; } public static bool SameValueEverywhere(SmallMatrix m) { return (m.m_a == m.m_b) && (m.m_a == m.m_c) && (m.m_a == m.m_d); } } static SmallMatrix s_smallMatrix; static void Watcher() { while (true) Debug.Assert(SmallMatrix.SameValueEverywhere(s_smallMatrix)); } static void Main(string[] args) { (new Thread(Watcher)).Start(); while (true) { s_smallMatrix = new SmallMatrix(0); s_smallMatrix = new SmallMatrix(1); } } } } 

请注意,在普通处理器上使用double值时无法观察到此行为,因为大多数x86指令的版本都使用64位块(例如movl

所以线程安全似乎不是IsNaN静态的好理由:

  1. 框架应该是平台无关的,所以它不应该以处理器架构之类的东西为前提。 IsNaN线程安全依赖于64bits值总是在目标体系结构上primefaces访问和修改的事实(并且Compact框架目标不是x86 ……)。
  2. IsNaN本身是无用的,并且在multithreading可以访问someVar的上下文中,此代码无论如何都是不安全的(无论IsNaN的线程安全性如何):
 print("code sample"); if (!double.IsNaN(someVar)) Console.WriteLine(someVar); 

我的意思是,即使IsNaN是通过对所有可能的NaN值进行==比较来实现的……(实际上并不可能)……谁关心在执行方法期间值的演变,如果它可能已经改变了一次方法终止…或者它甚至可能是一个中间值,如果目标体系结构不是x86,它应该永远不会在这里…

在两个不同的线程中访问内在值通常是不安全的,因此我认为通过在处理结构或任何其他类型时将任何方法设置为静态来提供一些安全错误的兴趣,

实例和static之间的区别是C#语言(和你说的Java)选择明确的基本点(在C ++中,你可以通过实例调用static方法,但这只是语法 – 在引擎实例下.StaticX与Class.StaticX实例相同。

向流畅的界面迈进已经开始解决了很多这个问题……

我记得导师的话,祈祷任何方法,不使用除参数之外的任何其他变量都是静态方法。

我真的不知道为什么,并没有想到后面,但从逻辑上讲,它似乎很好。 无论如何对答案感兴趣;-)

我认为马克正在回答这个问题。

问题是当您需要在valuetypes上调用实例方法时,该值将被加框。 这会导致严重的性能损失。

Double.IsNan遵循与String.IsNullorEmpty相同的模式。 后者的行为与它的行为一样,因为遗憾的是没有办法声明非虚拟实例方法应该与null“this”一起使用。 虽然这种行为对于可变引用类型可能很奇怪,但它对于必须是引用类型但在语义上应该像不可变值一样的行为非常有用。 例如,如果在null对象上调用其属性与在空字符串上调用它们的行为相同,则“String”类型会更方便。 实际上,有一个奇怪的上下文,其中空字符串对象将被视为空字符串,而尝试使用其中的字符串将产生错误。 如果字符串一直表现为一个初始化为空字符串的值类型,那将会更加清晰。