Question

This isn't really an optimization question or anything. It's essentially a what-the-heck-is-going-on-here kind of question. The singleton pattern is used to have a single instance of an object used multiple times. That's all well and good, but if I try to do a similar pattern with a struct, it don't get a single instance.

I came about this trying to do something with the Color struct in System.Drawing. Here is some example code:

    class Colors
    {
        private static Color _red;
        public static Color Red
        {
            get
            {
                if (_red.IsEmpty)
                    _red = Color.FromArgb(0xFF, 0xFF, 0x42, 0x39);

                return _red;
            }
        }
    }

    static void Main(string[] args)
    {
        var redOne = Colors.Red;
        var redTwo = Colors.Red;

        Console.WriteLine("redOne.Equals(redTwo) : {0}", redOne.Equals(redTwo));
        Console.WriteLine("redOne == redTwo : {0}", redOne == redTwo);
        Console.WriteLine("Object.Equals(redOne, redTwo) : {0}", Object.Equals(redOne, redTwo));
        Console.WriteLine("Object.ReferenceEquals(redOne, redTwo) : {0}", Object.ReferenceEquals(redOne, redTwo));
        Console.ReadLine();
    }

The output for this is:

redOne.Equals(redTwo) : True
redone == redTwo : True
Object.Equals(redOne, redTwo) : True
Object.ReferenceEquals(redOne, redTwo) : False

The first three results are to be expected, but the last is what surprised me. Now, my best guess is that when _red is returned from Colors.Red it's returning a copy, as normal value types do. So, while there is only a single instance of _red, Colors.Red returns a brand new instance, and that is what gets stored into redOne and redTwo. Is my thinking correct?

Also, if that is correct, is there any point in using the static keyword on structs?

Thanks

Was it helpful?

Solution

ReferenceEquals will not work with a struct/value type, only a class.

Calling ReferenceEquals on two structs will 'box' each struct into it's own memory location and then compare the two (obviously) different addresses.

Since a class is already a reference type, the call to ReferenceEquals compares the actual address of the instance.

Essentially you do not need to store a static copy of 'Red'. Change the code to the following:

class Colors
{
    public static Color Red
    {
        get
        {
            return Color.FromArgb(0xFF, 0xFF, 0x42, 0x39);
        }
    }
}

Additional reading: MSDN - Structs (C# Programming Guide)

OTHER TIPS

So, while there is only a single instance of _red, Colors.Red returns a brand new instance, and that is what gets stored into redOne and redTwo. Is my thinking correct?

Yes. You have two copies, since Color is a value type. When you use ReferenceEquals, you box the struct into an object, and call Object.ReferenceEquals on the two (distinctly different) boxed objects.

Also, if that is correct, is there any point in using the static keyword on structs?

The main point here isn't to make this a singleton - it's to simplify the API so you have what is effectively a constant value for a color: Colors.Red or Colors.Green. Without this being static, you would need to manually create a "red" color each time. The properties in this case are effectively a nicer looking factory method on the Colors class.

The variable _red will hold a single instance of Color that gets initialized once. Because .net does not provide any way to return a reference to a struct instance, however, every call to property Red will return a new struct instance whose fields, public and private, are initialized with values copied from _red. Further, because value type fields do not store objects, but merely data which can be implicitly copied to heap objects as needed (every value type has a corresponding heap object type; both are described by the same Type object, but they are not the same type), every time a value type is converted to a heap type (such as Object), a new heap object instance is created with field values initialized from the value-type instance.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top