Private Array is Implicitly Modified
CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 5 of 5

Thread: Private Array is Implicitly Modified

  1. #1
    Join Date
    Mar 2013
    Posts
    2

    Private Array is Implicitly Modified

    Hello, all. I'm new here (and to the C# programming language), so please excuse the format of my post for now.

    I've encountered a problem in a simple C# script I am writing. This is a test script and serves no major purpose beside understanding the fundamentals of classes and scope in C#. So here are the relevant details:

    1) I declared two private arrays inside a class called IntArray.
    2) The backup array, myIntArrayBackup, should be modified only once, when the IntArray object (list1) is instantiated, via the constructor.
    3) Every time I call public methods from the MainTest class using the IntArray object, the backup array is somehow implicitly being modified along with the myIntArray array that I am explicitly modifying. Any changes made to myIntArray are also being made to myIntArrayBackup.

    The way in which I am debugging this is very simple. In the main method I am calling the function Reset() which is supposed to restore the original array (taken in as a parameter when the list1 object is instantiated). But since the backup is constantly being modified, the original array is lost.

    The point is that the backup array should remain fixed as to what the IntArray constructor sets it to. Yet somehow its elements are always always equal to those in the non-backup (the arrays are essentially identical). Any ideas? The complete C# script is shown below for a complete reference:

    * Note that class Test2 is not relevant here

    Code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    
    namespace Testing
    {
    	class MainTest
    	{
    		static void Main(string[] args)
    		{
    			
    			// Test simple I/O
    			/*
    			Console.WriteLine("Hello World");
    			int x = int.Parse(Console.ReadLine());
    			Console.WriteLine("x = " + x++);
    			Console.WriteLine("x = " + x);
    			*/
    			
    			
    			// Test simple use of object and method
    			/*
    			Test2 test2obj = new Test2(18);
    			Console.WriteLine("PrintMe = " + test2obj.PrintMe(12));
    			*/
    			
    			int[] someIntArray = new int[] {9, 5, 2, 8, 33, 4, 7, 1, 8, 9};
    			IntArray list1 = new IntArray(someIntArray);
    			Console.WriteLine("Default order:");
    			list1.Print();
    			list1.SortAscending();
    			Console.WriteLine("Ascending order:");
    			list1.Print();
    			list1.Reset();
    			Console.WriteLine("Default order:");
    			list1.Print();
    			list1.SortDescending();
    			Console.WriteLine("Descending order:");
    			list1.Print();
    			
    			Console.ReadLine();
    		}
    	}
    	
    	class Test2
    	{
    		private int experiment10;
    		
    		public Test2(int experiment10)
    		{
    			this.experiment10 = experiment10;
    		}
    		
    		public int PrintMe(int val)
    		{
    			int printed = experiment10 - val;
    			return (printed);
    		}
    	}
    	
    	// Perform several functions on an array of integers
    	class IntArray
    	{	
    		private int[] myIntArray; // This array will be operated on
    		private int[] myIntArrayBackup; // This array will allow myIntArray to be reset
    	
    		public IntArray(int[] myIntArray)
    		{
    			this.myIntArray = myIntArray;
    			this.myIntArrayBackup = myIntArray;
    		}
    		
    		// Print each element in the array
    		public void Print()
    		{
    			foreach(int i in myIntArray)
    			{
    				Console.Write(i + " ");
    			}
    			Console.WriteLine();
    		}
    	
    		// Swap element at index1 with element at index2
    		public void Swap(int index1, int index2)
    		{
    			int temp;
    			temp = myIntArray[index1];
    			myIntArray[index1] = myIntArray[index2];
    			myIntArray[index2] = temp;
    		}
    	
    		// Sort all elements in ascending order
    		public void SortAscending()
    		{
    			int done = 0;
    			while(done == 0)
    			{
    				done = 1;
    				for(int i = 0; i < myIntArray.Length - 1; i++)
    				{
    					if(myIntArray[i] > myIntArray[i+1])
    					{
    						Swap(i, i+1);
    						done = 0;
    					}
    				}
    			}
    		}
    		
    		// Sort all elements in descending order
    		public void SortDescending()
    		{
    			int done = 0;
    			while(done == 0)
    			{
    				done = 1;
    				for(int i = 0; i < myIntArray.Length - 1; i++)
    				{
    					if(myIntArray[i] < myIntArray[i+1])
    					{
    						Swap(i, i+1);
    						done = 0;
    					}
    				}
    			}
    		}
    		
    		// Restore original array
    		public void Reset()
    		{
    			for(int i = 0; i < myIntArray.Length; i++)
    			{
    				myIntArray[i] = myIntArrayBackup[i];
    			}
    		}
    		
    	}
    	
    }

  2. #2
    Join Date
    Feb 2011
    Location
    United States
    Posts
    1,006

    Re: Private Array is Implicitly Modified

    This block of code assigns two variables (namely this.myIntArray and this.myIntArrayBackup) that both point at the exact same array data structure:

    Code:
    public IntArray(int[] myIntArray)
    {
        this.myIntArray = myIntArray;
        this.myIntArrayBackup = myIntArray;
    }
    That is, you've given one data structure two different names.

    Instead, make a copy:
    Code:
    public IntArray(int[] myIntArray)
    {
        //Create two new arrays
        this.myIntArray = new int[myIntArray.Length];
        this.myIntArrayBackup = new int[myIntArray.Length];
    
        //Actually make a copy
        Array.Copy(myIntArray, this.myIntArray, myIntArray.Length);
        Array.Copy(myIntArray, this.myIntArrayBackup, myIntArray.Length);
    }
    See:

    http://msdn.microsoft.com/en-us/library/k4yx47a1.aspx

    Make sense?
    Best Regards,

    BioPhysEngr
    http://blog.biophysengr.net
    --
    All advice is offered in good faith only. You are ultimately responsible for effects of your programs and the integrity of the machines they run on.

  3. #3
    Join Date
    Feb 2011
    Location
    United States
    Posts
    1,006

    Re: Private Array is Implicitly Modified

    Or, more explicitly, you can do an array copy like:

    Code:
    public IntArray(int[] myIntArray)
    {
        //Create two new arrays
        this.myIntArray = new int[myIntArray.Length];
        this.myIntArrayBackup = new int[myIntArray.Length];
    
        //Actually make a copy
        for(int i = 0; i < myIntArray.Length; i++)
        {
            this.myIntArray[i] = myIntArray[i];
            this.myIntArrayBackup[i] = myIntArray[i];
        }
    }
    In case the the Array.Copy(...) call was confusing.
    Best Regards,

    BioPhysEngr
    http://blog.biophysengr.net
    --
    All advice is offered in good faith only. You are ultimately responsible for effects of your programs and the integrity of the machines they run on.

  4. #4
    Join Date
    Mar 2013
    Posts
    2

    Re: Private Array is Implicitly Modified

    Thanks a lot! Your solutions have dismissed my problem.

    Incidentally I was using your explicit solution earlier, but without the block of code that creates the arrays. Is it the "new" keyword that allocates a new space in memory for these arrays?

  5. #5
    Join Date
    Jan 2010
    Posts
    1,102

    Re: Private Array is Implicitly Modified

    To understand this kind of problems find some info on the web about C# type system. Essentially, there are two major kinds of types in C# - reference types, which are types defined through classes, and value types, which are structs (including all of the fundamental types (such as bool, int, float, double), except for string). In an IDE, you can usually quickly distinguish between the two by hovering a mouse over a name, and a tool tip will tell you if the type is a class or a struct; other than that, fundamental types aside, in C# you'll be mostly working with reference types (classes).

    If you're familiar with the concept of pointers from other programming languages, reference types are a lot like pointers, except that the memory is managed, and the lifetime of objects is not deterministically up to you. So think of reference type variables as of pointing to (or referencing) the corresponding data somewhere in the memory. This data will not be garbage-collected (the memory will not be freed) as long as there are valid references to it from somewhere in your program. Once all references are lost (variables go out of scope, or you set them to null, etc.), the Garbage Collector (GC) will at some point in time, according to it's own schedule, do it's thing and free up the space.
    Now, unlike with pointers, you don't get to manipulate the addresses directly, and since reference type variables behave transparently, you don't need a special syntax (like the * and -> of c++) to dereference them.
    Also, reference type variables, when uninitialized, default to null.
    The important thing to note here that reference types are passed around (effectively) by reference - i.e. if you pass a reference type as a parameter to a method, the parameter will still point to the original object, and changes will persist after the method exits.

    For example:
    MyClass obj1 = new MyClass(); // creates an instance of MyClass
    MyClass obj2 = obj1; // BOTH obj1 and obj2 refer to the same object (kinda like pointers)
    obj1 = new MyClass(); // Now obj1 refers to a different object, but obj2 still refers to the original one
    obj2 = null; // Nothing refers to the original instance anymore; it's free to be garbage-collected

    In contrast, variables of a value types can be thought of as containing the data directly.
    Value types are always passed by value - that is, a method that has a value type parameter get's a local copy of the value.

    int val1 = 64; // roughly equivalent to: int val1 = new int(); val1 = 64;
    int val2 = val1; // val2 contains an INDEPENDENT COPY of val1

    Finally, to answer your question: the new keyword does allocate space in memory for all objects (arrays included), but if the array elements are reference types, they are basically just pointers, free to refer to nothing or anything, and the actual objects that are the elements of the array are stored elsewhere (and don't have to come into existence at the same time as the array).
    But, don't get to confused by that - just think of the new keyword as of a standard way to invoke the constructor of any type. In C++, the new keyword is used to dynamically allocate memory on the heap, which the programmer must later on manually free using the delete keyword. In C#, new is more abstract, and doesn't have such a meaning - it's simply a standard way to get a new instance; where the instance will be actually allocated is up to the JIT-compiler, and it's an internal implementation detail. Most often, structs end up on the stack, while classes are allocated on the managed heap, but this is less important; what's more important is to remember that structs are passed by value, and classes are passed by reference.

    Array types are reference types, but they are containers that can hold both value-typed and reference-typed elements. So, in the spirit of the examples above:
    int[] intArr1 = new int[5]; // an array instance with 5 int elements
    int[] intArr2 = intArr1; // essentially, now you have two names for the SAME array
    intArr2[3] = 5; // affects both

    Code:
       arr1 --+-- arr2
              |
              |              Elems:  
               `->[]-------> ######## 
                  []-------> ######## 
                  []-------> ######## 
                  []-------> ######## 
                  []-------> ######## 
                  []-------> ######## 
                  []-------> ########
    This is why you have to manually make a copy of the array. However, a more interesting case is when elements are of a reference type - then you can have two different arrays, with distinct references as elements, but both of them can be made to point to the same set of objects (like arrays of C++ pointers). You can't do this with value types. This is the reason why, when reference types are involved, you often need to make a deep copy - besides creating a new instance of the given array type, you need to copy the elements themselves as well.

    Shallow copy:
    Code:
    arr1:    refType Elems:    arr2:
     []-------> ######## <------[] 
     []-------> ######## <------[] 
     []-------> ######## <------[] 
     []-------> ######## <------[] 
     []-------> ######## <------[] 
     []-------> ######## <------[] 
     []-------> ######## <------[]
    Deep copy:
    Code:
    arr1:        Elems:       Copy:        arr2:
     []-------> ########    ######## <------[] 
     []-------> ########    ######## <------[] 
     []-------> ########    ######## <------[] 
     []-------> ########    ######## <------[] 
     []-------> ########    ######## <------[] 
     []-------> ########    ######## <------[] 
     []-------> ########    ######## <------[]
    Last edited by TheGreatCthulhu; March 9th, 2013 at 05:14 PM.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  


Windows Mobile Development Center


Click Here to Expand Forum to Full Width

This is a CodeGuru survey question.


Featured


HTML5 Development Center