12

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.

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). Granted it isn't really an issue for built-in types as the JIT could optimize this easilly.

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. (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)

Julien Roncaglia
  • 17,397
  • 4
  • 57
  • 75
  • 1
    It's slightly odd, I agree - and there are plenty of other situations which are like this, e.g. Char.IsDigit etc. I've never seen a good explanation :( – Jon Skeet Dec 16 '08 at 10:09

7 Answers7

14

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:

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

even though the structure is immutable. For thread safe methods use static methods.

Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168
Pop Catalin
  • 61,751
  • 23
  • 87
  • 115
  • any citations to support this? – Ruben Bartelink Dec 16 '08 at 11:09
  • 1
    @Ruben Bartelink, don't have a citation, just general CLR knowledge, read CLR via C# by Jeffrey Richter, he explains all that I said. – Pop Catalin Dec 16 '08 at 11:14
  • you can simply create a test application with a structure and and instance method, and you will see that the instance method is called with a managed pointer to the structure not by passing a copy, or better yet use the dissasembly window in VS to inspect and instance method of a primitive/struct – Pop Catalin Dec 16 '08 at 11:17
  • Obv too long ago since I read it (and Applied .NET and Box and Skeet, me brags...). Upvote atcha! – Ruben Bartelink Dec 16 '08 at 11:18
  • It's one good reason i think, even if it could have easily be done in an instance method (nothing keep an instance method from working on a local copy). And it also comfort me in my choice of IsNaN properties instead of a static method as i'm dealing with Immutable data structures anyway. – Julien Roncaglia Dec 16 '08 at 11:31
  • @VirtualBlackFox If the instance method works on a local copy then it doesn't have instance semantics ;), instance methods are supposed to have side effects on the object they operate on, that's why a pointer to the structure/primitive is passed and not a copy. – Pop Catalin Dec 16 '08 at 11:39
  • @Pop Catalin: Yes and no, property getters shouldn't have side effects. I agree that making a copy of yourself maybe counter-intuitive but it is a way as an other to ensure thread safety. – Julien Roncaglia Dec 16 '08 at 11:53
  • What i mean is that if a `IsNaN` property is implemented the caller doesn't care if it is thread safe because there is a lock, a local copy or anything else, he only care to know if it is (or not) thread-safe. – Julien Roncaglia Dec 16 '08 at 11:55
  • You are right for the non-thread safety of immutable struct, sorry i never reflected on that. – Julien Roncaglia Dec 16 '08 at 11:56
  • Hmm... I don't think this shows that immutable structs aren't threadsafe. I think it shows that a new value can be copied over a whole mutable struct, even though its individual fields cannot be modified, and that this can happen while a method is running. In order to do that, the struct has to... – Daniel Earwicker Dec 23 '08 at 17:34
  • ... be a member of a class. It's not obvious that 'bar' is a member of a class, but in fact it is. The compiler silently generates a hidden class because of the lambda. – Daniel Earwicker Dec 23 '08 at 17:36
  • @Earwicker it shows that structs always reside in volatile memory, and therefore instance methods are not thread safe, because instance methods access the struct through a pointer (a pointer to volatile memory). You can modify the app as you like (remove the lambda) and the result will be the same – Pop Catalin Dec 23 '08 at 17:41
  • Could you modify the code to show that? As far as I can make out, the problem is due to sharing the variable containing the value, which makes it possible to assign to that variable, changing the whole thing. So the variable has to be a member of a class, and then you are sharing one instance of ... – Daniel Earwicker Dec 23 '08 at 18:15
  • ... that class between two threads. With the class written explicitly you may find it easier to see that locking is obviously required to protect access to that storage location. Without that, no method of reading/writing the struct is threadsafe (static or instance). – Daniel Earwicker Dec 23 '08 at 18:17
  • I talked over this with some C# friends. A value type instance method operates on the object in-place - it doesn't copy it. While the method is running, it's possible for another thread to replace the struct with another object. I was already anti-struct; now even more. .. – Jay Bazuzi Dec 25 '08 at 04:41
  • ... but I will miss the free equality/hashcode behavior that you get with struct. I wish .Net equality wasn't so convoluted, and that C# made it easier to navigate. – Jay Bazuzi Dec 25 '08 at 04:41
9

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.


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. 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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • 1
    Extension methods/properties haven't yet registered well in my head.. yet. Nice use. – Gishu Dec 16 '08 at 10:17
  • (note; I added a test rig which shows that it does indeed inline perfectly; the performance over 2,000,000,000 calls is within a few ms (either can be quicker, which is what we expect)) – Marc Gravell Dec 16 '08 at 10:21
  • This general fluent extension instead of a static also has me creating a string.IsEmpty and string.IsNullOrEmpty as an instance – Ruben Bartelink Dec 16 '08 at 10:51
  • re the callvirt, as mentioned in other places, key diff between extension method and property is that in an extension you get to check if this == null, whereas the callvirt places a check for null and throw of NullReferenceException if it is. – Ruben Bartelink Dec 16 '08 at 10:53
  • @Mark the compiler never emits "callvirt" for methods on structures as it knows they can't be virtual ;) unless a "box" operand is emitted first. – Pop Catalin Dec 16 '08 at 11:11
  • @Pop - d'oh! You're right. I even loaded it into reflector to check, but I'm so "trained" to use class (rarely struct), that my fingers disobeyed me and wrote a class! – Marc Gravell Dec 16 '08 at 11:55
  • An instance method would box the primitive - thats the perf hit. maybe? – TheSoftwareJedi Dec 17 '08 at 00:02
  • 1
    @TheSoftwareJedi: boxing only occurs if "object" is involved. There is no need to box to call an instance method. – Marc Gravell Dec 17 '08 at 13:14
4

@Pop Catalin: I'm not ok with what you said in :

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.

So thread safety doesn't seem a good reason for IsNaN to be static :

  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...).
  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) :
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...

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,

Julien Roncaglia
  • 17,397
  • 4
  • 57
  • 75
  • Hmmm...guess I've might said it wrong, not thread safe, race condition free, once the struct is copied into the method stack, it can't be further modified, meaning if a static method does multiple operations on a struct, no race conditions will affect the value it recieved. – Pop Catalin Dec 17 '08 at 00:33
  • And besides there are many more issues with tread safety than even you said, things to consider are CPU caches, multi processors, etc. That doesn't mean static methods that operate on structs aren't thread safe. They are thread safe meaning they won't create race conditions. – Pop Catalin Dec 17 '08 at 00:40
  • Thread safety is not always about atomic operations is about 1) Not creating race conditions (Not IE modifying shared state) 2) not being affected by them (IE not working with shared state). Static methods do both 1) and 2). The static methods that accept values are thread safe ... – Pop Catalin Dec 17 '08 at 00:45
  • reading values and writing them is not, that's where you introduced a race in your application not in the static method. – Pop Catalin Dec 17 '08 at 00:46
  • My sample was writing values as yours was, if no one change the value where is the potential for race conditions ? What i meant is that static methods guard you against change while the method execute, but the change could also have happened while the copy to the stack was in progress. – Julien Roncaglia Dec 17 '08 at 10:22
  • The static method guarantee the caller that the method won't modify the struct behind his back, but i don't see any implementation of IsNaN modifying state. => I agree with your *1*, i just don't see why it could apply here. – Julien Roncaglia Dec 17 '08 at 10:26
  • As said previously my problem is with your *2* : Who care that IsNaN be guarded against race conditions inside itself? – Julien Roncaglia Dec 17 '08 at 10:30
  • I mean two choice here : You guarded the value with a lock or any other mechanism and so this guarantee is useless *OR* you haven't guarded it and the race condition may have happened while copying the value to the stack of the static IsNaN so this guarantee is useless. – Julien Roncaglia Dec 17 '08 at 10:33
  • Granted that it can't happen to double values on x86 systems as they are copied to the stack in one atomic operation. But it is dependent on architecture and doesn't apply to stucts. – Julien Roncaglia Dec 17 '08 at 10:38
  • "Who care that IsNaN be guarded against race conditions inside itself?" Because not doing so, creates security holes in a application. What happens if parameters passed to a method are modified after the method has determined they are safe? A method that writes to the file system for example. – Pop Catalin Dec 17 '08 at 12:52
  • Also passing a struct by value to a static method guarantees that the value the method received won't change, from method entry to method exit, this is where thread safety come from, not from reading the value and passing it to the method, which still need to be done atomically. – Pop Catalin Dec 17 '08 at 12:57
0

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.

The move towards fluent interfaces has started unravelling a lot of this though...

Ruben Bartelink
  • 59,778
  • 26
  • 187
  • 249
  • Yes Marc and sorry VBF, made a big botchjob on my initial response - didnt get the fact that you guys had that well covered and were considering things much more deeply than I was giving credit for! (My edits make you point less clear, but it definitely is correct - my bad!) – Ruben Bartelink Dec 16 '08 at 10:49
0

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

Oliver Friedrich
  • 9,018
  • 9
  • 42
  • 48
  • I remember Scott Meyers saying that (altough about C++). Whether this was related to some performance or technical thing or just "taste" - I can't recall ;-) – Christian.K Dec 16 '08 at 11:24
  • Calling a static method with an boject parameter or an instance parameterless method should be exactly the same. The only thing that shouldn't be done is an instance method that isn't using the instance at all (And even this could be needed in some cases). – Julien Roncaglia Dec 16 '08 at 11:37
0

I think Marc was onto the answer.

The problem is when you need to call instance methods on valuetypes, the value is boxed. This would cause a severe performance penalty.

leppie
  • 115,091
  • 17
  • 196
  • 297
  • False, read chapter 13.3 of ECMA-335 (CIL) : """s the this pointer. By contrast, instance and virtual methods of value types shall be coded to expect a managed pointer (see Partition I) to an unboxed instance of the value type. """ – Julien Roncaglia Dec 16 '08 at 22:01
0

Double.IsNan follows the same pattern as 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". 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. 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.

supercat
  • 77,689
  • 9
  • 166
  • 211