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);
4

3 回答 3

2

结构应该是不可变的。您不应该能够修改结构中的属性。修改的正确方法myVector是为属性分配一个新实例。

如果你想创建一个更方便的 API,我建议你这样设计你的结构:

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);
于 2013-04-20T23:17:32.433 回答
0

结构通常应该代表以下两件事之一:

  • 一个统一的实体,例如分数或时刻(例如 aDecimalDate)。

  • 用胶带粘在一起的一小部分固定的自变量集合,有时可以单独使用,有时可以作为一个组使用(例如 3d 点的坐标),并且除了它们包含的值之外没有任何标识(例如,任何Point3d具有值 [1,4,7] 等价于具有相同值的任何其他值)。

Microsoft 的指导原则适用于第一种类型的东西时很好,但是编写它们的人没有考虑到一些有用的概念比第一种模式更适合第二种模式。从事物的声音来看,您的数据类型符合第二种模式。因此,它应该将其整个状态公开为公共字段(您可以这样做)。此外,在许多情况下,如果可行,它应该作为ref参数传递。如果将暴露字段类作为ref参数传递,则接收者将能够像修改相应类型的单个变量一样容易地修改其字段。不幸的是,没有将属性公开为ref参数的机制。除非您编写自己的方法将结构传递为ref参数并使用它们而不是属性,您最好的选择可能是执行以下操作:

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

另一种选择(如果有很多字段,这可能是有利myVector的)是有一个someThing类似的方法:

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);
}

想要添加 4 到Xof的调用者myVector可以写

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

如果调用者有一个vector被调用velocity并且它想添加一个垂直于它的向量,something.myVector它可以这样做:

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

请注意,使用ref具有开放字段结构的参数可能非常有效,因为传递结构所需的时间ref与其大小无关。不幸的是,所有框架集合都没有内置任何此类工具,而 C# 并没有做太多使此类代码变得方便的工作。然而,即使使用temp变量的方法也比使用所谓的“不可变”结构或不可变类的代码更简洁、更有效。

于 2013-04-22T19:18:32.770 回答
0

虽然之前所说的是真的——你应该让你的结构不可变——默认情况下它们不是这样的。你可以在你拥有的设置中改变一个结构。

我最初说你需要初始化你的结构,然后注意到它是。所以我回去重新评估这个问题,并意识到我的想法不对。

问题是值类型的语义。当你这样做

me.myVector

您将获得结构值的副本,而不是对其的引用。然后随后尝试在同一行中修改该结构上的值是没有意义的。

下面的代码可能会使它更清晰:

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();
}

因此,在此之后,这里是如何创建一个可变值类型,您的代码可以在其中工作(请注意,该myVector属性必须是可直接访问的字段,或者是您在MutateStruct调用中访问的具有支持字段的属性):

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();
    }
}
于 2013-04-20T23:23:31.260 回答