Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C#
  4. Disparity between value semantics behavior with List< struct > vs. array of struct

Disparity between value semantics behavior with List< struct > vs. array of struct

Scheduled Pinned Locked Moved C#
csharpvisual-studiolinqdesigndata-structures
3 Posts 2 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    Stache
    wrote on last edited by
    #1

    I may be having dense programmer moment, but I just spent an hour debugging something that came down to a really unexpected (by me, at least) behavior with List<> vs. array. If you create a struct with a method that modifies a member, and invoke that method on an instance of the struct that resides within an array, the array element is modified as you would expect. But if you perform the identical operation on the struct that is contained in a List, the item in the List is NOT modified. The following program illustrates this...the effect is observable in VS 9 and 10. Does this seem correct or reasonable? It certainly doesn't adhere to one of my favorite principles of language design: the principle of least surprise! thanks, jim

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Diagnostics;

    namespace TestByValue
    {
    class Program
    {
    static void Main(string[] args)
    {
    Foo[] aArray = new Foo[3];
    aArray[0].Increment();

            Console.WriteLine("aArray\[0\].x is {0} and should be {1}", aArray\[0\].x, 1);
            Debug.Assert(aArray\[0\].x == 1);
    
            List<Foo> aList = new List<Foo>();
            aList.Add(new Foo());
            aList\[0\].Increment();
    
            Console.WriteLine("aList\[0\].x is {0} and should be {1}", aList\[0\].x, 1);
            Debug.Assert(aList\[0\].x == 1);      // This Assert fires
        }
    
        struct Foo
        {
            public int x;
    
            public void Increment()
            {
                x++;
            }
        }
    }
    

    }

    A 1 Reply Last reply
    0
    • S Stache

      I may be having dense programmer moment, but I just spent an hour debugging something that came down to a really unexpected (by me, at least) behavior with List<> vs. array. If you create a struct with a method that modifies a member, and invoke that method on an instance of the struct that resides within an array, the array element is modified as you would expect. But if you perform the identical operation on the struct that is contained in a List, the item in the List is NOT modified. The following program illustrates this...the effect is observable in VS 9 and 10. Does this seem correct or reasonable? It certainly doesn't adhere to one of my favorite principles of language design: the principle of least surprise! thanks, jim

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Diagnostics;

      namespace TestByValue
      {
      class Program
      {
      static void Main(string[] args)
      {
      Foo[] aArray = new Foo[3];
      aArray[0].Increment();

              Console.WriteLine("aArray\[0\].x is {0} and should be {1}", aArray\[0\].x, 1);
              Debug.Assert(aArray\[0\].x == 1);
      
              List<Foo> aList = new List<Foo>();
              aList.Add(new Foo());
              aList\[0\].Increment();
      
              Console.WriteLine("aList\[0\].x is {0} and should be {1}", aList\[0\].x, 1);
              Debug.Assert(aList\[0\].x == 1);      // This Assert fires
          }
      
          struct Foo
          {
              public int x;
      
              public void Increment()
              {
                  x++;
              }
          }
      }
      

      }

      A Offline
      A Offline
      Anthony Mushrow
      wrote on last edited by
      #2

      Normally in C# most things are passed by reference so when you access the item in your list and call Increment on it you modify the actual object in the list. However in C# structs are value types (like an Int for example) and when they are passed around what you actually get are copies. So if you had a list of Ints and you accessed them in your list you wouldn't get the actual object from the list, you just get a copy that you can do whatever you like with. If you modified the Int you'd have to actually set it back to the list to actually make any changes:

      List myList = new List();
      myList.Add(5);
      myList.Add(2);
      //myList == {5, 2}

      int number = myList[1];
      number += 3;
      //myList == {5, 2}, like you would expect
      myList[1] = number;
      //myList == {5, 5}

      With a normal array you are actually accessing the items directly so in your case they get modified as expected, but with a generic list the array subscript operator is the same as calling a function like

      int GetAtIndex(int i)
      {
      return theActualList[i];
      }

      And because the struct isn't passed by reference what you end up with is a copy of what's stored in the list and that's what you're actually modifying. If you wanted it to work the same for both then you could simply change your struct for a class http://msdn.microsoft.com/en-us/library/ms173109.aspx[^] Well I hope that makes sense, it's rather late and I'm off to bed now ;P And I also hope I haven't made and blindingly obvious mistakes.

      My current favourite quote is: Punch them in the face, see what happens!

      -SK Genius

      modified on Thursday, June 3, 2010 11:28 PM

      S 1 Reply Last reply
      0
      • A Anthony Mushrow

        Normally in C# most things are passed by reference so when you access the item in your list and call Increment on it you modify the actual object in the list. However in C# structs are value types (like an Int for example) and when they are passed around what you actually get are copies. So if you had a list of Ints and you accessed them in your list you wouldn't get the actual object from the list, you just get a copy that you can do whatever you like with. If you modified the Int you'd have to actually set it back to the list to actually make any changes:

        List myList = new List();
        myList.Add(5);
        myList.Add(2);
        //myList == {5, 2}

        int number = myList[1];
        number += 3;
        //myList == {5, 2}, like you would expect
        myList[1] = number;
        //myList == {5, 5}

        With a normal array you are actually accessing the items directly so in your case they get modified as expected, but with a generic list the array subscript operator is the same as calling a function like

        int GetAtIndex(int i)
        {
        return theActualList[i];
        }

        And because the struct isn't passed by reference what you end up with is a copy of what's stored in the list and that's what you're actually modifying. If you wanted it to work the same for both then you could simply change your struct for a class http://msdn.microsoft.com/en-us/library/ms173109.aspx[^] Well I hope that makes sense, it's rather late and I'm off to bed now ;P And I also hope I haven't made and blindingly obvious mistakes.

        My current favourite quote is: Punch them in the face, see what happens!

        -SK Genius

        modified on Thursday, June 3, 2010 11:28 PM

        S Offline
        S Offline
        Stache
        wrote on last edited by
        #3

        Thanks, SK...that's very clear, though I do think that having anObject[i].Increment() behave differently according to whether anObject is an array of Foo vs. List when Foo is a struct is error prone. I actually think I would prefer that array indexing behaved the same way...that is, the indexer returns a copy. Now that I've had time to sleep on it, I remember that a few years ago I fought through creating an indexer for my own particular struct and came to realize that I had no choice but to return a copy...so I was getting the exact same semantics as occurs with List. Maybe I won't fall into the same trap again in a couple of years. But I wouldn't count on it! :laugh: cheers, jim

        1 Reply Last reply
        0
        Reply
        • Reply as topic
        Log in to reply
        • Oldest to Newest
        • Newest to Oldest
        • Most Votes


        • Login

        • Don't have an account? Register

        • Login or register to search.
        • First post
          Last post
        0
        • Categories
        • Recent
        • Tags
        • Popular
        • World
        • Users
        • Groups