1

I'm new to C# and I don't understand how to achive something like this (as I would do in a language like C++):

List<Element> _elements = ... // Created before
for (var i = 0; i < _elements.Count; i++)
{
    _elements[i].Color = Color.Black;
}

It gives me: Indexer access returns temporary value. Cannot modify struct member when accessed struct is not classified as a variable, which I find weird because I have specified the field with { get; set; }, shouldn't the example above "use" the setter?

I only get it to work by creating a new instance and copying the other values of my class Element (which contains Color and Value), which is weird, because the error message above said indexer returns a temporary value that I would then just overwrite. It also feels wrong, and would be tedious for any larger data types:

List<Element> _elements = ... // Created before
for (var i = 0; i < _elements.Count; i++)
{
    _elements[i] = new Element(_elements[i].Value, Color.Black);
}

Is the problem that I am using List? Or should I iterate in some other way?

Element class is super simple:

public struct Element
{
    public long Value { get; set; }
    public Color Color { get; set; }

    public Element(long value, Color color)
    {
        Value = value;
        Color = color;
    }
}

Thanks in advance.

EDIT This was edited to better show that this question is not about assignment, but that _elements is already created.

saffronjam
  • 59
  • 1
  • 5
  • 3
    In the title you say "class" field, but it's not a class (reference type), it's a struct (value type). That's basically the big difference. – Pac0 Dec 20 '20 at 22:24
  • 3
    You'd have to make a local copy, mutate that and then assign it back like `var temp = _elements[i]; temp.Color = Color.Black; _element[i] = temp;` however mutable structs are evil as you run into issues like this. – juharr Dec 20 '20 at 22:30
  • Should `Element` be a `struct` in the first place? Have you read the differences between classes and struct in c#? – Camilo Terevinto Dec 20 '20 at 22:31
  • 1
    `which I find weird because I have specified the field with { get; set; }, shouldn't the example above "use" the setter?` a) That is a property, not a field. b) It isn't complaining about the `.Color` it is complaining about the `_elements[i].Color` _as a whole_. The compiler is rightfully pointing out "hey this thing you are doing makes no sense whatsoever, so I won't let you do it". – mjwills Dec 20 '20 at 22:33
  • 1
    `struct` is a valuetype. When you access this struct using indexer, it creates a temporary value and assigns it to that value. Due to it being valuetype, this assignment causes a copy of the value. So now you have a copy of a value, completely independent of the struct that's inside your List, you modify it and nothing will happen. – JohanP Dec 20 '20 at 22:34
  • 1
    This makes a lot of sense. Basically making Element a class solves the problem. My big mistake here was litte bit overlooking the big difference between structs and classes in C# versus C++ (which I wrote a bit before starting C#). Thus making this question seem rather poorly researched before asking it. I appriciate your time and clarification. Thanks a lot. And I will take a deeper dive into the difference right away. – saffronjam Dec 20 '20 at 22:37

3 Answers3

4

The problem is that you are using a struct (value type), not a class (reference type).

So, when you write _elements[i], you don't have a reference to the same object that is in the list, you have a whole copy of the Element.

This difference between class/struct is also noticeable when you pass parameters of functions.

Here are some possibilities:

Option 1:

Use a class instead of a struct.

Option 2a:

You can reassign the "slot" in the list, _elements[i] = new Element(....) you already noticed that.

It also feels wrong, and would be tedious for any larger data types:

A bit, see 2b.

Option 2b:

Just use a temporary variable and reassign the value,

var temp = _elements[i];
temp.Color= Color.Black;
_elements[i] = temp;

no need to recreate another new Element value here.

Pac0
  • 21,465
  • 8
  • 65
  • 74
1

If you want to "modify class field" in your for-loop, then create a class not a struct -

public class Element    // this one is a class, not a struct
{
    public long Value { get; set; }
    public Color Color { get; set; }

    public Element(long value, Color color)
    {
        Value = value;
        Color = color;
    }
}

As for the distinction, take a look at this post - What's the difference between struct and class in .NET?

atiyar
  • 7,762
  • 6
  • 34
  • 75
  • While this correctly answers my question, I've made an edit to it that ``_elements`` was created before and not right before the for-loop, thus clarifying that the question was not about assignment. Thanks for answer and the link, definitely need to read up on that :) – saffronjam Dec 20 '20 at 22:53
  • @viesa, Ok, I'll remove the suggestion then :) – atiyar Dec 20 '20 at 23:12
-2

I was working this out and in the process of posting when a couple of comments came in that said basically the same things. This may help to understand better what comments are stating.

List<Element> _elements = new List<Element>(5);

for (var i = 0; i < 5; i++)
{
    Element E =new Element();
    E.Color = Colors.MediumAquamarine;

    _elements.Add(E);
}
Jeff
  • 646
  • 1
  • 7
  • 13