C# 为什么IsNan是双类上的静态方法而不是实例属性?
问题在标题中,为什么:C# 为什么IsNan是双类上的静态方法而不是实例属性?,c#,oop,C#,Oop,问题在标题中,为什么: return double.IsNaN(0.6d) && double.IsNaN(x); 而不是 return (0.6d).IsNaN && x.IsNaN; 我这样问是因为当实现自定义结构时,它具有与NaN相同的特殊值,我倾向于使用第二个 此外,属性的性能通常更好,因为它避免了复制堆栈上的结构来调用IsNaN静态方法(并且由于我的属性不是虚拟的,所以没有自动装箱的风险)。当然,对于内置类型来说,这并不是一个真正的问题,因为JIT可
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
”调用实例方法,即使该类型被密封为不可空。因此,让它保持静态可能会带来性能方面的好处?幸运的是,扩展方法仍然是静态的,因此您可以保留这种行为。实例和静态
之间的区别是C#语言(以及您所说的Java)选择明确的一个基本点(C++中,可以通过实例调用<代码>静态<代码>方法,但这只是在引擎下的语法。STATICX是一个相同的实例类。Stasx.< /P>
向fluent接口的转变已经开始解决很多问题,尽管……静态方法是线程安全的,基本体上的方法通常需要线程安全的,以支持平台中的线程(意味着至少不受内部竞争条件的影响),实例方法采用指向结构的托管指针,这意味着在方法执行时可以同时修改结构/原语,另一方面,静态方法采用结构/原语的副本,因此不受线程竞争条件的影响 如果结构是线程安全的,那么这些方法只有在执行原子操作时才应该成为实例方法,否则应该选择静态方法 (作为另一种选择,可以使用使用锁定的实例方法,但它们比复制更昂贵) Edit:@virtualblackbox我已经准备好了一个示例来说明结构上的实例方法即使在不可变结构上也不是线程安全的:
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);
}
}
}
显示实例方法打印:
0
五,
即使结构是不可变的。对于线程安全方法,使用静态方法。我记得一位导师的话,他祈祷任何不使用参数以外任何其他变量的方法都是静态方法 我真的不知道为什么,也没有想过背后的原因,但从逻辑上来说,似乎不错。
对答案感兴趣;-)@Pop Catalin:我不同意你在: 如果结构是线程安全的,那么这些方法只有在执行原子操作时才应该成为实例方法,否则应该选择静态方法 下面是一个小程序,它演示了静态方法不能解决结构的这个问题:
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);
}
}
}
}
请注意,在公共处理器上使用双值无法观察到这种行为,因为大多数x86指令的版本都使用64位块,例如movl
因此,线程安全性似乎不是IsNaN保持静态的好理由:
someVar
的上下文中,此代码无论如何都是不安全的(不管IsNaN的线程安全性如何):在两个不同的线程中访问intristic值通常是不安全的,因此我认为在处理结构或任何其他类型时,通过将任何方法放在静态状态来提供某种安全错觉是没有兴趣的,我认为Marc已经找到了答案
问题是当您需要对valuetypes调用实例方法时,该值被装箱。这将导致严重的性能损失。Double.IsNan遵循与String.IsNullorEmpty相同的模式。后者的行为与String.IsNullorEmpty相同,因为遗憾的是,无法声明非虚拟实例方法应可用于null“this”。虽然这种行为对于可变引用类型来说可能很奇怪,但对于必须是引用类型但在语义上应该表现为不可变值的事物,它将非常有用。例如,“String”如果在null对象上调用其属性的行为与在空字符串上调用属性的行为相同,则类型将更加方便。事实上,在大量的上下文中,null字符串对象将被视为空字符串
print("code sample");
if (!double.IsNaN(someVar))
Console.WriteLine(someVar);