Home > Back-end >  How can I make a constant static immutable instance of a class?
How can I make a constant static immutable instance of a class?

Time:03-08

I have a simple class that represents 3 dimensional coordinates called Coord3 that simply has x, y, and z int values.

I want to declare a static constant variable Coord3.zero where x, y, and z are set to 0.

I have attempted this with:

public static readonly Coord3 zero = new Coord3(0, 0, 0);

However I found that this variable can be changed. For example if I do

Coord3 coord = Coord3.zero;
coord.x = 5;

this actually changes the x value of Coord3.zero to be 5. Maybe I am misunderstanding readonly? I know that in Unity there is Vector3.zero which never changes. I am trying to achieve the same effect.

CodePudding user response:

readonly is not quite the same thing as immutable in the sense you mean. readonly means you cannot assign to a variable. So you couldn't do

public static readonly Cord3 zero = new Cord3(0, 0, 0);

zero = new Cord3(0, 0, 1);

In order to achieve the effect you want, you could need to create a class, struct or record with readonly properties or fields. There's no way to achieve that effect with a type defined in an internal library. If the type allows mutability on a field or property, that field or property is mutable.

CodePudding user response:

Maybe I am misunderstanding readonly?

Yeah, readonly means you can't change the reference of your variable. In other words, you can't write Coord3.zero = new(...);

Now, the way these things are usually written is as structs, where fields are by default immutable. That would solve your problem right there. That is also how it's done in Unity. Note that you can also do this with classes, by having only getters on your properties and filling them in once from your constructor, but classes are very heavy weight for these small types.

CodePudding user response:

Marking zero as readonly indeed prevents you from changing what zero stores. You cannot reassign it.

zero = new Coord3(1, 1, 1); // error

Note that since Coord3 is a class, zero.x = 5; isn't actually changing what zero stores. You are just changing some property of the object that zero is referring to. zero is still storing the same reference to the same old object.

You could prevent this by not providing any public API in Coord3 that would change its fields' values - for example, by making x, y, z all read-only properties:

public int X { get; }
public int Y { get; }
public int Z { get; }

Of course, this wouldn't work if you just want to prevent setting the properties on zero, but allow the modification of Coord3 on other objects.

I would suggest that you make Coord3 a struct:

public struct Coord3 {
    public int X { get; set; }
    public int Y { get; set; }
    public int Z { get; set; }
}

Now zero stores the fields' values directly, rather than a reference to an object. zero.x = 5; would produce an error, because you are modifying what zero directly stores.

Note that Unity's Vector3 for example, is also a struct.

CodePudding user response:

I am trying to achieve the same effect.

For This effect, You can use it this way.

 void Main()
{
   Coord3 zero = new Coord3();
   zero.Zero();  
}

public  class Coord3
{ 
    public Coord3()
    {
            
    }
    public Coord3(int x,int y,int z)
    {
        X=x;
        Y=y;
        Z=z;
    }
    public  void Zero(){
        X=0;
        Y=0;
        Z=0;
    }
    public int X { get; set; }
    public int Y { get; set; }
    public int Z { get; set; }
}

CodePudding user response:

I think you can make an immutable base class and your Coord3 inheriting this class.

public class BaseCoord3
{
    // protected means it can only be used by BaseCoord3 and Coord3
    protected int x;

    // equivalent to public int X { get { return x; } }
    public int X => x;  
}

public class Coord3 : BaseCoord3
{
    public override int X
    {
        get { return x; }
        set { x = value; }
    }
    public static BaseCoord3 Zero => new BaseCoord3(0,0,0);
}

This should work similar to the way with Readonly versions of collections in c#. I think the struct solutions are the way to go though.

CodePudding user response:

How about you just use a get-property to never change the zero object?

public class Coord3
{
    public static Coord3 Zero => new Coord3(0,0,0);
}

Then you won't be able to change the values of Zero, but you will maintain the functionality of Coord3 objects.

Coord3 a = Coord3.Zero;
a.x = 2; // changes a.x, but not Coord3.Zero.x
  •  Tags:  
  • c#
  • Related