"Cannot modify a value type return" Explanation Please

Hey Guys
Im back with more noob questions. Could someone please explain to me this Error, from what Ive found on the internet Im setting and getting at the same time and should create a temp var. Im not really clear how or why. Please help me

Thank heaps , hopefully I can contribute back at some point lol

Assets/Scripts/GridScripts/GridDistribution.cs(97,56): error CS1612: Cannot modify a value type return value of `NodeDistribution.nodeAttrDistribution’. Consider storing the value in a temporary variable

Assets/Scripts/GridScripts/GridDistribution.cs(102,57): error CS1612: Cannot modify a value type return value of `NodeDistribution.nodeAttrDistribution’. Consider storing the value in a temporary variable

Assets/Scripts/GridScripts/GridDistribution.cs(104,57): error CS1612: Cannot modify a value type return value of `NodeDistribution.nodeAttrDistribution’. Consider storing the value in a temporary variable

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GridDistribution : GridBase, IGridDistCellAuto<GridDistribution> {

    public NodeDistribution nodeDistribution;
    public NodeDistribution[,] gridNodeDistribution;

    public int width;
    public int Width
    {
        get
        {
            return gridSizeX;
        }

    }
    public int height;
    public int Height
    {
        get
        {
            return gridSizeY;
        }
    }
    public string seed;
    public string Seed
    {
        get
        {
            return seed;
        }
        set
        {
            seed = value;
        }
    }
    public bool useRandomSeed;
    public bool UseRandomSeed
    {
        get
        {
            return useRandomSeed;
        }
        set
        {
            useRandomSeed = value;
        }
    }
    public bool invert;
    public bool Invert
    {
        get
        {
            return invert;
        }
        set
        {
            invert = value;
        }
    }
    [Range(0,100)]
    public int randomFillPercent;
    public int RandomFillPercent
    {
        get
        {
            return randomFillPercent;
        }
        set
        {
            randomFillPercent = value;
        }
    }


    // Use this for initialization
    void Start () {

        // Create a grid
        gridNodeDistribution = CreateGridT<NodeDistribution> (nodeDistribution, gridNodeDistribution);

    }

    // Inferance methods                       
    public void RandomFillMapT<T>( T[,] gridIn ) where T : GridDistribution{
        if (useRandomSeed) {
            seed = Time.time.ToString();
        }

        System.Random pseudoRandom = new System.Random(seed.GetHashCode());

        for (int x = 0; x < width; x ++) {
            for (int y = 0; y < height; y ++) {
                if (x == 0 || x == width-1 || y == 0 || y == height -1) {
                    gridIn[x,y].nodeDistribution.nodeAttrDistribution.filled = 1; 
                }
                else {
                    if (invert)

                        gridIn[x,y].nodeDistribution.nodeAttrDistribution.filled  = (pseudoRandom.Next (0, 100) < randomFillPercent) ? 0 : 1;
                    else {
                        gridIn[x,y].nodeDistribution.nodeAttrDistribution.filled  = (pseudoRandom.Next (0, 100) < randomFillPercent) ? 1 : 0;
                    }
                }
            }
        }
    }

}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;



public interface IGridDistCellAuto<T> {

    int        Width{get;}
    int     Height{get;}
    string     Seed{get; set;}
    bool     UseRandomSeed{get; set;}
    bool     Invert{get; set;}
    int     RandomFillPercent{get; set;}

    // Randomly fill our grid
    void RandomFillMapT<T>( T[,] gridIn ) where T : GridDistribution;


}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public struct NodeAttrDistribution {

    public int filled;               
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

[System.Serializable]
public class NodeDistribution : NodeBase {


    private NodeAttrDistribution _nodeAttrDistribution;                                               
    public  NodeAttrDistribution  nodeAttrDistribution    {
        get
        {
            return _nodeAttrDistribution;
        }
        set
        {
            _nodeAttrDistribution = value;
        }
    }



    //
    // Constructor running base class constuctor
    public NodeDistribution(Vector3 worldPosition, int gridX, int gridY) 
        : base( worldPosition,  gridX,  gridY) {


    }
        

}

Your class ‘NodeDistribution’ has a property call ‘nodeAttrDistribution’ that returns a ‘NodeAttrDistribution’ struct.

The problem is how structs work combined with how properties work.

Properties are syntax that allow you to have getter/setter functions that syntactically look like fields, but aren’t fields.

When you access a field, you directly access the value in its spot in memory.

When you access the getter of a property, it returns the value. In the case of a ref type (class) this is fine. But in the case of a value type (struct)… there’s a problem. Returning a struct from a function (or getter method), returns a COPY.

You’re no longer modifying the field ‘_nodeAttrDistribution’ you’re modifying a copy of that field, returned from ‘nodeAttrDistribution’.

This is the same reason why you can’t say:

transform.position.x = 5f;

position returns a copy of the position vector, rather than directly referencing the position.

So this line:

gridIn[x,y].nodeDistribution.nodeAttrDistribution.filled  = (pseudoRandom.Next (0, 100) < randomFillPercent) ? 0 : 1;

and the line in the else statement as well… need to instead be:

var attr = gridIn[x,y].nodeDistribution.nodeAttrDistribution;
attr.filled = (pseudoRandom.Next (0, 100) < randomFillPercent) ? 0 : 1;
gridIn[x,y].nodeDistribution.nodeAttrDistribution = attr;

On a side note… why is ‘filled’ typed as an int? But appears to be used as a boolean? Why not use boolean?

1 Like

I see , ok this make sense. Yea it should be a bool that is an oversight on my end. Let me digest this and refactor my code. I guess im still not sure when to use a property vs a method.

Currently I have a node class that adds struct nodeAttr via properties. The node is instance into a grid class so the struct makes sense to me. I feel like Im forcing this properties thing

Thanks so much for taking the time to explain this to me.