繁体   English   中英

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

[英]Why IsNan is a static method on the Double class instead of an instance property?

问题在标题中,为什么:

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

代替

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

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

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

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

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

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

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

编辑:@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

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

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

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 ”,即使其类型被密封为非可空。 那么将它静态化可能会带来性能上的好处吗? 幸运的是,扩展方法仍然算作静态,因此您可以保留此行为。

@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);
            }
        }
    }
}

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

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

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

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

在两个不同的线程访问intristic值不是一般的安全,所以我看到通过把静态的任何方法提供安全的一些幻想没有兴趣与结构或任何其他类型的交易时,

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

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM