CodeGuru Home VC++ / MFC / C++ .NET / C# Visual Basic VB Forums Developer.com
Results 1 to 7 of 7
  1. #1
    Join Date
    Aug 2007
    Posts
    858

    [RESOLVED] References and copying objects

    I'm used to the C++ world where there's a fairly clear distinction between dealing with an object directly and dealing with a pointer or reference... so maybe I'm overthinking things somewhat, but I just want to be clear in my understanding before I confuse myself with the 'everything is a reference' notion....

    I have a Message class, and I need to be able to make (deep) copies of it, such that if I have Message A, and make a copy of it, Message B, A and B have identical content, but any change to B has no effect on A. Easiest to show some code:

    Code:
    class Variable {}
    
    class Message
    {
      public String Title {get; set;}
      public String Text {get; set;}
      public Dictionary<String, Variable> Variables
      {
        get { return _variables; }
      }
      
      private Dictionary<String, Variable> _variables;
      
      Message() {}
      
      Message(Message copy)
      {
        //Is this.Title a new string?  or referencing the same string as copy.Title?
        Title = copy.Title;
        Text = copy.Text;
        
        //is this a deep copy or not?
        _variables = new Dictionary<String, Variable>(copy._variables);
      }
    }

  2. #2
    Join Date
    Jun 2011
    Location
    Office: Guilford, VT & Northfield, MA
    Posts
    10

    Re: References and copying objects

    I see here at this code that you made a class named Message.

    Is it that you want these properties:
    public String Title {get; set;}
    public String Text {get; set;}
    public Dictionary<String, Variable> Variables
    {
    get { return _variables; }
    }
    initialized (set) to the values via this method?

    Message(Message copy)
    {
    //Is this.Title a new string? or referencing the same string as copy.Title?
    Title = copy.Title;
    Text = copy.Text;

    //is this a deep copy or not?
    _variables = new Dictionary<String, Variable>(copy._variables);
    }

    I think by the looks of it that you are not referencing the copy.Title, because if you were then it would have to be like this:
    Message(ref Message copy)
    {
    Title = copy.Title;
    Text = copy.Text;

    _variables = new Dictionary<String, Variable>(copy._variables);
    }

  3. #3
    Join Date
    May 2007
    Posts
    1,546

    Re: References and copying objects

    //Is this.Title a new string? or referencing the same string as copy.Title?
    This is the same object reference. The exact same string. As strings are immutable, this is fine.
    //is this a deep copy or not?
    _variables = new Dictionary<String, Variable>(copy._variables);
    Check the documentation for this constructor. You'll find that it copies all the elements from the dictionary you pass in. i.e. the two dictionaries are physically different, but they contain the exact same objects as their keys and values.
    www.monotorrent.com For all your .NET bittorrent needs

    NOTE: My code snippets are just snippets. They demonstrate an idea which can be adapted by you to solve your problem. They are not 100% complete and fully functional solutions equipped with error handling.

  4. #4
    Join Date
    Jun 2011
    Location
    Office: Guilford, VT & Northfield, MA
    Posts
    10

    Re: References and copying objects

    //is this a deep copy or not?
    _variables = new Dictionary<String, Variable>(copy._variables);
    }


    Whenever you use the "new" keyword, it allocates memory to make a new object with the propeties of the copy._variables. So it is different copy here.

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

    Re: References and copying objects

    It's not so confusing with C# as you might think.

    There are two kinds of types in C#: value types, and reference types.
    Forget about all the talk emphasizing that one kind is allocated on the stack, an the other on the heap, because this is neither terribly important, nor entirely true.
    What is important is the semantics each kind carries.

    Value types directly represent the actual values, and are tossed around by value - i.e. they are copied. All the "fundamental" types (bool, int, long...) are value types, as well as all the user-defined structs. These are usually types that have a very small memory footprint and are designed to be passed around a lot. Since IntelliSense in C# works like a charm, unlike it's problematic C++ counterpart (or the non-existent C++/CLI version of VS2010), you can use it to check if some type is a struct or not.

    Reference types, on the other hand, internally maintain a reference to the actual object. Note that we (the devs) don't actually know what this "reference" exactly is or how it is implemented, and this construct is not directly exposed to the language as such - so don't confuse them (in terms of underlying mechanism) with C++ references, or pointers. They just indirectly point to the actual object somehow.
    Reference types are classes and delegates. In C#, you'll develop reference types 90&#37; of the time.
    They are, obviously, passed by reference. Well, to be more precise, the reference itself is copied (passed by value), but as it still points to the same object, the object is, in effect, passed by reference.

    Now, before I get to your actual question, there is one more thing of importance every C# programmer should be aware of. The String type, although it appears to behave as a value type at first glance, is actually a reference type.
    However, it is immutable.
    This is important to know, because sometimes it can affect performance.
    StringBuilder provides a mutable string functionality in that case.
    I'll come back to strings later.

    OK.
    Consider this simple example:
    Code:
    MyClass x = new MyClass("instance 1");
    MyClass y = x;
    
    Console.WriteLine(x.Name);    // outputs: "instance 1"
    Console.WriteLine(y.Name);    // outputs: "instance 1"
    As expected, both x and y refer to the same object. Assuming this object is mutable, if you do something like this:
    Code:
    MyClass x = new MyClass("instance 1");
    MyClass y = x;
    
    y.Name = "Speedo";
    
    Console.WriteLine(x.Name);    // outputs: "Speedo"
    Console.WriteLine(y.Name);    // outputs: "Speedo"
    Both of them will be affected. If, on the other hand, you do this:
    Code:
    MyClass x = new MyClass("instance 1");
    MyClass y = x;
    
    y.Name = "Speedo";
    
    Console.WriteLine(x.Name);    // outputs: "Speedo"
    Console.WriteLine(y.Name);    // outputs: "Speedo"
    
    y = new MyClass("Harry Potter");
    
    Console.WriteLine(x.Name);    // outputs: "Speedo"
    Console.WriteLine(y.Name);    // outputs: "Harry Potter"
    As you see, y now refers to a completely different instance, while x points to the old one.

    With value types, the object is just copied.
    Strings are somewhat special. String is a reference type, but it is immutable by design. As an optimization, two variables containing the same logical string, also point to the same object. But every time you change the value of the variable, that variable points to a different instance.

    So in effect, although
    Title = copy.Title;
    Text = copy.Text;
    point to the same string instance at the assignment-time, if the container objects (this and copy) aren't referencing one an the same instance of Message, then changing the Title or Text property on one will not affect the other - because you can never change (mutate) the actual string, but only assingn a new one.
    It's like if you had a pointer in C++, and a set-method, that would assign an address of a new object to that pointer.

    Code:
        //is this a deep copy or not?
        _variables = new Dictionary<String, Variable>(copy._variables);
    In the most trivial case, new invokes a constructor that creates a new instance. With objects that contain other objects, you'll need to check the documentation (as you would need to do so in C++, for that matter).

    For this specific constrictor of Dictionary, MSDN says:
    Initializes a new instance of the Dictionary<TKey, TValue> class that contains elements copied from the specified IDictionary<TKey, TValue> and uses the default equality comparer for the key type.
    So: yes.

    P.S. To avoid confusion, a few comments on passing with the ref keyword, mentioned by DemSamPro. It is roughly equivalent to C++'s passing by reference via "&". For value types, it will make the object to be passed by reference, so that any changes (internal "mutations") to it will be preserved when the method returns. For reference types, it will make the reference itself to be passed by reference, so that if you assign a different instance to the variable, i.e change to what object it refers to, the original will point to this new object after the method returns. Akin to ref-to-pointer in C++.
    These features are rarely used, and there's certainly no need to use them here.
    Last edited by TheGreatCthulhu; July 7th, 2011 at 02:08 PM.

  6. #6
    Join Date
    Jan 2010
    Posts
    1,133

    Re: References and copying objects

    Whoops!

    Quote Originally Posted by TheGreatCthulhu View Post
    Code:
        //is this a deep copy or not?
        _variables = new Dictionary<String, Variable>(copy._variables);
    In the most trivial case, new invokes a constructor that creates a new instance. With objects that contain other objects, you'll need to check the documentation (as you would need to do so in C++, for that matter).

    For this specific constrictor of Dictionary, MSDN says:

    So: yes.
    As soon as I wrote that, a seed of doubt was planted in my mind - something was off.
    Then I used ILSpy to decompile the Dictionary<TK, TV> class, and check out what it actually does? How it could know what's the appropriate way to copy every possible object? As, it turns out, it doesn't copy anything...
    As always, it's best when you try it out yourself, so I went and set up this code as a test:

    Code:
        class Program
        {
            static void Main(string[] args)
            {
                NamedEntity x = new NamedEntity("Neo");
                NamedEntity y = new NamedEntity("Morpheus");
                NamedEntity z = new NamedEntity("Trinity");
    
                Dictionary<int, NamedEntity> dict = new Dictionary<int, NamedEntity>();
                dict.Add(x.ID, x);
                dict.Add(y.ID, y);
                dict.Add(z.ID, z);
    
                foreach (NamedEntity e in dict.Values)
                    Console.WriteLine(e.ID.ToString() + ": " + e.Name);
    
                Dictionary<int, NamedEntity> dict1 = new Dictionary<int, NamedEntity>(dict);
    
    
                dict1[0].Name = "Agent Smith";
                z.Name = "The Oracle";
    
                
                Console.WriteLine();
    
                foreach (NamedEntity e in dict.Values)
                    Console.WriteLine(e.ID.ToString() + ": " + e.Name);
                
                foreach (NamedEntity e in dict1.Values)
                    Console.WriteLine(e.ID.ToString() + ": " + e.Name);
            }
        }
    
        public class NamedEntity
        {
            private static int s_idGen = 0;
    
            private int m_id;
    
            public NamedEntity(string name)
            {
                m_id = s_idGen;
                s_idGen++;
    
                Name = name;
            }
    
            public string Name { get; set; }
            
            public int ID 
            {
                get { return m_id; }
            }
        }
    Output:
    0: Neo
    1: Morpheus
    2: Trinity

    0: Agent Smith
    1: Morpheus
    2: The Oracle
    0: Agent Smith
    1: Morpheus
    2: The Oracle

    So, as it turns out, it's a shallow copy after all!
    Actually, that is probably not all that surprising.

    The worst part: the example provided by Microsoft at MSDN is rather vague as to what exactly they mean by "copy", and they are using strings - which are immutable.
    Replacing a string just replaces the instance.

    In my example, I would get the same result, if I did this:
    Code:
    dict1[0].Name = "Agent Smith";
    z.Name = "The Oracle";
    
    // NEW CODE: assign a different instance...
    dict1[0] = new NamedEntity("Merovingian");
    I would get this output instead (note that the integers uniquely identify each instance; also, each dictionary contains 3 elements):
    0: Agent Smith
    1: Morpheus
    2: The Oracle
    3: Merovingian
    1: Morpheus
    2: The Oracle

    So you need to write your own routine that would create the copies, and then add them to a new dictionary.
    Last edited by TheGreatCthulhu; July 7th, 2011 at 03:33 PM.

  7. #7
    Join Date
    Aug 2007
    Posts
    858

    Re: References and copying objects

    Thanks for the replies, great info.

Posting Permissions

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





Click Here to Expand Forum to Full Width

Featured