0

How do i modify a field in a property that is a struct?

For example:

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
    struct vector
    {
        public vector(int theX, int theY)
        {
            x = theX;
            y = theY;
        }
        public int x;
        public int y;
    }

    class SomeClass
    {
        public vector myVector { get; set; }
        public SomeClass()
        {
            myVector = new vector(10, 20);
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            SomeClass me = new SomeClass();
            me.myVector.x = 200; //Error

            Console.Read();
        }
    }
}

If the vector was a class, then i would be able to modify it.

So my question is: How can i modify it if it was a struct?

So far, my solution would be setting my current vector to a new vector

for example (if i only wanted to modify the x value):

me.myVector = new vector(200,me.myVector.y);
Assassinbeast
  • 1,207
  • 1
  • 17
  • 33
  • You should not modify a struct and leave it immutable. See this so for why: http://stackoverflow.com/questions/3751911/why-are-c-sharp-structs-immutable – Justin Pihony Apr 20 '13 at 23:14

3 Answers3

2

Structs are supposed to be immutable. You shouldn't be able to modify a property in your struct. The correct way to modify myVector would be to assign a new instance to the property.

If you want to create a more convenient API, I suggest you design your struct like this:

public struct vector
{
    private readonly int x;
    private readonly int y;

    public vector(int theX, int theY)
    {
        x = theX;
        y = theY;
    }

    public int X { get { return this.x; } }
    public int Y { get { return this.y; } }

    public vector SetX(int x) 
    {
        return new vector(x, this.y);
    }

    public vector SetY(int y) 
    {
        return new vector(this.x, y);
    }
    .. other operations
}


me.myVector = me.myVector.SetX(200);
p.s.w.g
  • 146,324
  • 30
  • 291
  • 331
0

While what has been said before is true - you should make your structs immutable - they aren't that way by default. You CAN mutate a struct in the setup that you have.

I originally said that you needed to initialize your struct, which was then noted that it was. So I went back and re-evaluated the problem, and realized I wasn't thinking right.

The issue is the semantics of a value type. When you do

me.myVector

you're getting a copy of the value of the struct, not a reference to it. Then subsequently trying to modify a value on that struct in the same line doesn't make sense.

This below code may make it a bit clearer:

static void Main(string[] args)
{
    SomeClass me = new SomeClass();
    //me.myVector.x = 200; //Error

    // Get a local copy of the struct, which is NOT a reference to the me.myVector variable.
    vector myVec = me.myVector;
    // Now mutate the value of the local struct
    myVec.x = 3;
    int xNew = myVec.x; // xNew = 3

    int xOld = me.myVector.x; // xOld still = 10

    Console.Read();
}

So, following on to this, here is how you can make a mutable value type, in which your code would work (note that the myVector property must be a directly accessible field, or be a property with a backing field which you access in the MutateStruct call):

class SomeClass
{
    public vector myVector;
    public SomeClass()
    {
        myVector = new vector(10, 20);
    }

    public void MutateStruct(int newX)
    {
        myVector.x = newX;
    }
}

class Program
{
    static void Main(string[] args)
    {
        SomeClass me = new SomeClass();

        me.MutateStruct(200);
        int newerX = me.myVector.x; // newerX is now 200

        Console.Read();
    }
}
nawfal
  • 70,104
  • 56
  • 326
  • 368
Gjeltema
  • 4,122
  • 2
  • 24
  • 31
0

Structs should generally represent one of two things:

  • A single unified entity, such as a fractional number or a moment in time (e.g. a Decimal or Date).

  • A small fixed collection of independent variables stuck together with duct tape, which may sometimes be used individually and sometimes as a group (e.g. the coordinates of a 3d point), and which have no identity beyond the values they contain (so that e.g. any Point3d with values [1,4,7] is equivalent to any other with those same values).

Microsoft's guidelines are good when applied to things of the first type, but whoever wrote them failed to consider that some useful concepts fit the second pattern much more so than the first. By the sound of things, your data type fits that second pattern. As such, it should have its entire state exposed as public fields (which you do). Further, it should in many cases, when practical, be passed as a ref parameter. If an exposed-field class is passed as a ref parameter, the recipient will be able to modify its fields about as easily as it could modify single variables of the corresponding types. Unfortunately, there is no mechanism for exposing properties as ref parameters. Unless you write your own methods which pass structures as ref parameters and use those instead of properties, your best bet will probably be to do something like:

var temp = something.myVector;
temp.X += 200;
something.myVector = temp;

an alternative (which could be advantageous if myVector had many fields) would be to have a method in someThing like:

delegate void ActByRef<T1>(ref T1 p1);
delegate void ActByRef<T1>(ref T1 p1, ref T2 p2);

void modifyMyVector(ActByRef<myVector> actor) 
{
  actor(ref _myVector); // _myVector must be manual prop with backing field
}
void modifyMyVector<TX1>(ActByRef<myVector, TX1> actor, ref TX1 px1) 
{
  actor(ref _myVector, ref px1);
}

A caller that wanted to e.g. add 4 to X of myVector could write

something.ModifyMyVector((ref myVector vec) => vec.X+=4;)

If the caller had a vector called velocity and it wanted to add a vector perpendicular to that to something.myVector it could do:

something.ModifyMyVector((ref myVector vec, ref myVector vel) => 
  { vec.X += vel.Y; vec.Y -= vel.X; }, ref velocity);

Note that using ref parameters with open-field structures can be very efficient, since the time required to pass a struct by ref is independent of its size. Unfortunately, none of the Framework collections have any such facilities built in, and C# doesn't do much to make such code convenient. Even the approach using the temp variable is apt to be cleaner and more efficient, however, than code using so-called "immutable" structures or immutable classes.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • Although some programmers who don't understand value-type semantics might not like mutable structs, that hardly means they're "supposed" to be immutable. Why do you suppose the designers of .NET went through so much trouble to support piecewise-mutable value semantics if they were never supposed to be used? While there are times when it makes sense to have value types which can only be mutated by overwriting the whole thing (e.g. `Double` or `Decimal`), there are times when a it makes sense to attach a collective meaning to a group of variables that are also meaningful individually. – supercat Apr 22 '13 at 23:16
  • Also, the term "immutable struct" is a misnomer. The statement `Struct1 = Struct2;` mutates `Struct1` by overwriting its fields with the values of corresponding fields in `Struct2`. `Struct1` will still refer *the same instance* after the assignment as it did before. Boxed value type instances always have mutable reference semantics, even if the value types in question claim to be "immutable", since assignment, boxing, and byref generation all happen outside the type's control. – supercat Apr 22 '13 at 23:31