Gentlefolk,

Using C# in VS 2005.

I have a struct (some components deleted for simplicity) :

public struct CoOrdData
      {
        public int[] coOrdMaxArray;  // = new int[3]; // XYZ co-ord maximum 
		..
		..
      }

This is used in the following objects:

public CoOrdData GraphData  = new CoOrdData();      
    public CoOrdData trackGraph = new CoOrdData();

In the constructor for the surrounding class I have:

// Graph parameters,set the size (3) of the array
       GraphData.coOrdMaxArray  = new int[3];
       trackGraph.coOrdMaxArray = new int[3];

This is done to establish the size of the array as you cant set the size in the struct.

All compiles clean, however during execution of an event on the surrounding class the following code corrupts things,

trackGraph.coOrdMaxArray[0] = GraphData.coOrdMaxArray[0] + (midX - adjX);

trackGraph.coOrdMaxArray[0] AND GraphData.coOrdMaxArray[0] are both set to (midX - adjX)!!!!!!

Extensive reading about value/reference and any example/tutorials on arrays and structures has not helped.

Any soultions/ideas/comments appreciated, Ian

Dani AI

Generated

Short answer: the array inside your struct is a reference type, so assigning one struct to another copies the array reference, not the array contents. As noted, that is why updating an element via one variable shows up in the other.

A simple, local fix is to make an explicit deep-copy of the array when you copy the struct. For example, add a copy method on the struct that clones the int[] (Clone returns an object, so cast it back):

public CoOrdData DeepCopy()
{
    var copy = this;                     // copies value fields and references
    if (coOrdMaxArray != null)
    {
        copy.coOrdMaxArray = (int[])coOrdMaxArray.Clone();
    }
    return copy;
}

Then replace plain assignment with a deep copy when you need independent arrays:
trackGraph = GraphData.DeepCopy();

Design notes and alternatives:

  • If you actually want shared state, make CoOrdData a class and keep the array as-is.
  • If you want true value semantics, avoid storing mutable reference types inside a struct — use three ints (X,Y,Z) or a small value-type container instead of an int[].
  • Mutable structs are a common source of subtle bugs; prefer small, immutable structs or use classes for larger/mutable data.

Quick checklist for debugging:

  • Search for any accidental GraphData = trackGraph-style assignments during initialization.
  • If performance matters, prefer Array.Copy/Buffer.BlockCopy for larger buffers.
  • Add unit tests that assert identity vs. equality of arrays to catch accidental shared references.

Recommended Answers

All 3 Replies

Do you ever have a statement to the effect of

GraphData = trackGraph;

or vice versa? Because from that point forward, you'll be referencing the same object in memory (or so the program will have you believe).

class Program
    {
        static void Main(string[] args)
        {
            CoOrdData GraphData = new CoOrdData();
            CoOrdData trackGraph = new CoOrdData();

            GraphData.coOrdMaxArray = new int[3];
            trackGraph.coOrdMaxArray = new int[3];

            GraphData = trackGraph;

            int midX = 50;
            int adjX = 10;
            trackGraph.coOrdMaxArray[0] = GraphData.coOrdMaxArray[0] + (midX - adjX);

            Console.WriteLine(trackGraph.coOrdMaxArray[0]);
            Console.WriteLine(GraphData.coOrdMaxArray[0]);

            Console.Read();
        }
    }

    public struct CoOrdData
    {
        public int[] coOrdMaxArray;
    }

That sort of goes against what you think of with structs, being value types. You should be able to say GraphData = trackGraph without issue. In fact, if you remove the array and use just an integer, you can do just that. The code below keeps the structs independent of one another:

class Program
    {
        static void Main(string[] args)
        {
            CoOrdData GraphData = new CoOrdData();
            CoOrdData trackGraph = new CoOrdData();

            GraphData = trackGraph;

            int midX = 50;
            int adjX = 10;
            trackGraph.Storage = GraphData.Storage + (midX - adjX);

            Console.WriteLine(trackGraph.Storage);
            Console.WriteLine(GraphData.Storage);

            Console.Read();
        }
    }

    public struct CoOrdData
    {
        public int Storage;
    }

So what's the deal? Well, as it turns out, your structs in either case are still unique. However, it is the reference to the array that is getting hosed. You set the one struct equal to the other, the fields and references copy over. Your value-type fields are still independent, but now your array references are pointing to the same objects in memory.

class Program
    {
        static void Main(string[] args)
        {
            CoOrdData GraphData = new CoOrdData();
            CoOrdData trackGraph = new CoOrdData();

            GraphData.ArrayStorage = new int[3];
            trackGraph.ArrayStorage = new int[3];

            GraphData = trackGraph;

            int midX = 50;
            int adjX = 10;
            trackGraph.Storage = GraphData.Storage + (midX - adjX);
            trackGraph.ArrayStorage[0] = 15;

            Console.WriteLine(trackGraph.Storage); // writes 40
            Console.WriteLine(GraphData.Storage); // writes 0, still unique
            Console.WriteLine(trackGraph.ArrayStorage[0]); // writes 15
            Console.WriteLine(GraphData.ArrayStorage[0]); // writes 15 again

            Console.Read();
        }
    }

    public struct CoOrdData
    {
        public int Storage;
        public int[] ArrayStorage;
    }
commented: Shows great knowledge. +6

Thank you for your rapid and detailed response. Yes I did have a statement "trackGraph = GraphData", it was executed back in some initialisation code.

How to fix/get-around the problem?

It looks Ok if rather than move the entire object with "trackGraph = GraphData" I move the array components of the struct individually as for example:

trackGraph.coOrdMaxArray[0] = GraphData.coOrdMaxArray[0]

Ugly, but as it is only done once it is managable.

Once again thanks, Ian

To simplify your life a bit, you can use Array.CopyTo() to expedite matters.

int[] array1 = new int[3] {1,2,3};
    int[] array2 = new int[3] {0,0,0};
    array1.CopyTo(array2, 0);

One line to copy it, but the arrays are still independent of one another.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.