0

I am new to C#, so bear with me. I have a problem in c# where I can't decide on whether I need a class or a struct. I'm creating a List of either the class or struct and adding elements to it. If I use a struct, then I can add an item, alter it, and add it again and the changes will be seen because it is passed by value. The problem is, structs are not mutable so I can't edit any of the elements later. If I use a class, then if I add an item and alter it, all the items in the list get changed. I have to create a new instance of the object each time if I want it to be different. But I have an object that I only want to change one item in, so I have to copy all the other items to the new object?! WHY? Is there a better solution?

This code illustrates my problem:

namespace TestError
{
    public partial class Form1 : Form
    {
        private List<Thing> lst;
        private Thing obj;

        public Form1()
        {
            InitializeComponent();

            lst = new List<Thing>();
            obj = new Thing();

            obj.a = "bla";
            lst.Add(obj);
            //obj = new Thing();
            obj.a = "thing";
            lst.Add(obj);

            foreach (Thing t in lst)
                listBox1.Items.Add(t.a);
        }
    }

    class Thing
    {
        public string a;
        //problem is there are many more items here that don't change!
    }
}

Why the struct doesn't work:

namespace TestError
{
    public partial class Form1 : Form
    {
        private List<Thing> lst;
        private Thing obj;

        public Form1()
        {
            InitializeComponent();

            lst = new List<Thing>();
            obj = new Thing();

            obj.a = "bla";
            lst.Add(obj);
            lst[0].a = "new";  //error. if i change it to class, it works.
            obj.a = "thing";
            lst.Add(obj);

            foreach (Thing t in lst)
                listBox1.Items.Add(t.a);
        }
    }

    struct Thing
    {
        public string a;
    }
}
Synaps3
  • 1,597
  • 2
  • 15
  • 23
  • "structs are not mutable" is not really true statement - unfortunately `struct` are mutable just fine. Usually people work hard to achieve immutability (maybe you did too - but there is no code that shows your `struct`). – Alexei Levenkov Jun 28 '13 at 06:22

2 Answers2

0

Class is the way to go. I've never touch the struct type since I left college.

For the Thing class, you stated" there are many more items here that don't change!". Is that mean the values are fixed or the values are based on the initial object. If the values are fixed, you can assigned some default values in the class.

If the values are based on the initial object, I don't see the hassle of passing the variable while adding the object to the list.

CalebC
  • 922
  • 11
  • 24
  • The values are not constant and they are not based on the initial object either. In the real program, the object is created in the constructor and the values of the object are changed randomly at different times in the program. Then when the Commit method is executed, the object is added to the list. The problem with class is I end up with all items the same in the list. I can't just create a new object after the Commit method because not every value is changed every time, so I'd be left with some null values that I want retained. – Synaps3 Jun 28 '13 at 06:54
  • Let me try to understand the scenario. An object is created with random values periodically, and when you commit you going to add them into the list. I'm sure you can add some conditions before adding the object to the list. – CalebC Jun 28 '13 at 07:03
0

The problem is, structs are not mutable so I can't edit any of the elements later.

This is simply not true; don't get me wrong, I think it is a really bad idea to do what you are trying to here, and I think a class with a Clone() method would be the best solution, but the reason you are thinking this is precisely due to value-type semantics. You need to keep in mind that list indexers (the [0]) are methods - and not direct access; consider:

lst[0].a = "new";

what this line would do, if it compiled, is:

  • fetch a copy of the object out of the list (this is now a completely isolated and separate copy)
  • change a field on the copy
  • discard the copy

The compiler knows that this pretty certainly is not what you intended, so it prevents you making a mistake; the usage there would be:

var tmp = lst[0];
tmp.a = "new";
lst[0].a = tmp;

Oddly enough, though - if lst were an array, then that would work - arrays allow for direct in-situ editing:

lst[0].a = "new";

would compile. HOWEVER, PLEASE DON'T DO THIS:

  • mutable structs are a bad idea and will cause lots of confusion in your code
  • public fields are also a bad idea
  • large structs are also a bad idea

Like I say, my advice here would be a class; if you want convenient copy semantics, then add a Clone() method

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900