简体   繁体   English

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

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

The question is in the title, why : 问题在标题中,为什么:

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

Instead of 代替

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

I ask because when implementing custom structs that have a special value with the same meaning as NaN I tend to prefer the second. 我问,因为在实现具有与NaN相同含义的特殊值的自定义结构时,我倾向于选择第二种结构。

Additionally the performance of the property is normally better as it avoid copying the struct on the stack to call the IsNaN static method (And as my property isn't virtual there is no risk of auto-boxing). 此外,属性的性能通常更好,因为它避免复制堆栈上的结构以调用IsNaN静态方法(并且因为我的属性不是虚拟的,所以没有自动装箱的风险)。 Granted it isn't really an issue for built-in types as the JIT could optimize this easilly. 当然,对于内置类型来说,这不是一个真正的问题,因为JIT可以很容易地优化它。

My best guess for now is that as you can't have both the property and the static method with the same name in the double class they favored the java-inspired syntax. 我现在最好的猜测是,因为你不能在double类中同时拥有属性和静态方法,所以他们更喜欢java启发的语法。 (In fact you could have both as one define a get_IsNaN property getter and the other an IsNaN static method but it will be confusing in any .Net language supporting the property syntax) (事实上​​你可以同时定义一个get_IsNaN属性getter,另一个定义一个IsNaN静态方法,但它会混淆任何支持属性语法的.Net语言)

Static Methods are thread safe, methods on primitives generally need to be thread safe to support threading in the platform (meaning at least safe from internal race conditions), instance methods take a managed pointer to a structure, meaning that the structure/primitive might be modified concurrently while the method executes, on the other hand static methods take a copy of the structure/primitive and therefore are safe from threading race conditions. 静态方法是线程安全的,基元上的方法通常需要是线程安全的,以支持平台中的线程(意味着至少安全地从内部竞争条件开始),实例方法采用托管指向结构的指针,这意味着结构/原语可能是另一方面,静态方法采用结构/原语的副本,因此在线程竞争条件下是安全的。

If the structure is intended to be thread safe, then the methods should be made instance methods only if they do atomic operations, else static methods should be chosen. 如果结构是线程安全的,那么只有在进行原子操作时才应该将方法设为实例方法,否则应选择静态方法。

(As another option, instance methods that use locking could be used but they are more expensive than, copying) (作为另一种选择,可以使用使用锁定的实例方法,但它们比复制更昂贵)

Edit: @VirtualBlackFox I've prepared and example to show that instance methods on structures are not thread safe even on immutable structures: 编辑:@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);
        }
    }
}

The display Instance method prints: 0 5 显示Instance方法打印:0 5

even though the structure is immutable. 即使结构是不可变的。 For thread safe methods use static methods. 对于线程安全方法,使用静态方法。

Interesting question; 有趣的问题; don't know the answer - but if it really bugs you, you could declare an extension method, but it would still use the stack etc. 不知道答案 - 但如果它真的让你烦恼,你可以声明一个扩展方法,但它仍然会使用堆栈等。

static bool IsNaN(this double value)
{
    return double.IsNaN(value);
}

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

It would be nicer (for the syntax) if C# had extension properties, but the above is about the closest you can get at the moment, but it should "inline" quite well anyway. 如果C#具有扩展属性会更好(对于语法),但是上面是关于你现在可以得到的最接近的,但它应该“内联”得相当好。


Update; 更新; thinking about it, there is another difference between static and instance; 想一想,静态和实例之间还有另一个区别; C# always calls instance methods with " callvirt " rather than " call ", even if ther type is sealed an non-nullable. C# 总是使用“ callvirt ”调用实例方法而不是“ call ”,即使其类型被密封为非可空。 So perhaps there is a performance benefit from having it static? 那么将它静态化可能会带来性能上的好处吗? Luckily, extension methods still count as static, so you get to retain this behavior. 幸运的是,扩展方法仍然算作静态,因此您可以保留此行为。

@Pop Catalin: I'm not ok with what you said in : @Pop Catalin:我对你所说的不满意:

If the structure is intended to be thread safe, then the methods should be made instance methods only if they do atomic operations, else static methods should be chosen. 如果结构是线程安全的,那么只有在进行原子操作时才应该将方法设为实例方法,否则应选择静态方法。

Here is a small program that demonstrate that static methods don't solve this problem for structs : 这是一个小程序,演示静态方法不能解决结构的这个问题:

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

Note that this behavior can't be observed with double values on common processor as most x86 instructions have a version working with 64bits chunks such as movl . 请注意,在公共处理器上使用double值时无法观察到此行为,因为大多数x86指令的版本都使用64位块(例如movl

So thread safety doesn't seem a good reason for IsNaN to be static : 所以线程安全似乎不是IsNaN静态的一个很好的理由:

  1. The framework should be platform agnostic and so it shouldn't presuppose things like the processor architecture. 框架应该是平台无关的,所以它不应该以处理器架构之类的东西为前提。 IsNaN thread-safety is dependent on the fact that 64bits values are always accessed and modified atomicaly on the target architecture (And Compact framework targets aren't x86...). IsNaN线程安全依赖于64bits值总是在目标体系结构上原子访问和修改的事实(并且Compact框架目标不是x86 ...)。
  2. IsNaN is useless by itself and in a context where multiple thread could access someVar this code is anyway unsafe (regardless of the thread safety of IsNaN) : IsNaN本身是无用的,并且在多线程可以访问someVar的上下文中,此代码无论如何都是不安全的(无论IsNaN的线程安全性如何):
print("code sample");
if (!double.IsNaN(someVar))
    Console.WriteLine(someVar);

What i mean is that even if IsNaN is implemented by doing == comparisons with all possible NaN values... (not really possible) ...who care that the value evolve during the execution of the method if anyway it could have changed once the method terminate... or it could even be an intermediate value that should never have been here if the target architecture isn't x86... 我的意思是,即使IsNaN是通过对所有可能的NaN值进行==比较来实现的......(实际上并不可能)......谁关心在执行方法期间值的演变,无论如何它可能已经改变了一次方法终止...或者它甚至可以是一个中间值,如果目标体系结构不是x86,它应该永远不会在这里...

Accessing intristic values in two different threads is NOT safe in general so i see no interest in providing some illusion of safety by putting any method static when dealing with structs or any other type, 在两个不同的线程访问intristic值不是一般的安全,所以我看到通过把静态的任何方法提供安全的一些幻想没有兴趣与结构或任何其他类型的交易时,

The distinction between an instance and a static is a fundamental point which the C# language (and Java as you say) chooses to make clear (in C++, you can call a static method through an instance, but thats just syntax - under the hood instance.StaticX is the same saying instance Class.StaticX. 实例和static之间的区别是C#语言(和你说的Java)选择明确的基本点(在C ++中,你可以通过实例调用static方法,但这只是语法 - 在引擎实例下.StaticX与Class.StaticX实例相同。

The move towards fluent interfaces has started unravelling a lot of this though... 向流畅的界面迈进已经开始解决了很多这个问题......

I remember the words of a mentor, praying that any method, that does not use any other variables than the parameters are static methods. 我记得导师的话,祈祷任何方法,不使用除参数之外的任何其他变量都是静态方法。

I really do not know why, and havent thought on the behind, but logically, it seems good. 我真的不知道为什么,并没有想到后面,但从逻辑上讲,它似乎很好。 Interested in the answer anyhow ;-) 无论如何对答案感兴趣;-)

I think Marc was onto the answer. 我认为马克正在回答这个问题。

The problem is when you need to call instance methods on valuetypes, the value is boxed. 问题是当您需要在valuetypes上调用实例方法时,该值将被加框。 This would cause a severe performance penalty. 这会导致严重的性能损失。

Double.IsNan follows the same pattern as String.IsNullorEmpty. Double.IsNan遵循与String.IsNullorEmpty相同的模式。 The latter behaves as it does because there is regrettably no way to declare that a non-virtual instance method should be usable with a null "this". 后者的行为与它的行为一样,因为遗憾的是,没有办法声明非虚拟实例方法应该与null“this”一起使用。 While such behavior might be odd for mutable reference types, it would be very useful for things that have to be reference types but which should behave semantically like immutable values. 虽然这种行为对于可变引用类型可能是奇怪的,但对于必须是引用类型但在语义上应该像不可变值一样的事物来说它将非常有用。 For example, the "String" type would have been much more convenient if invoking its properties on a null object would have behaved identically to invoking them on an empty string. 例如,如果在空对象上调用其属性与在空字符串上调用它们的行为相同,则“String”类型会更方便。 As it is, there's an odd mish-mosh of contexts in which a null string object will be regarded as an empty string, and those in which trying to use one will generate an error. 实际上,有一个奇怪的上下文,其中空字符串对象将被视为空字符串,而尝试使用其中的字符串将产生错误。 It would have been much cleaner if string had behaved consistently as a value type which is initialized to an empty string. 如果字符串一直表现为一个初始化为空字符串的值类型,那将会更加清晰。

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

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