如何修改属性中的字段

时间:2013-04-20 23:11:43

标签: c# properties struct field

如何修改属性结构中的字段?

例如:

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

如果向量是一个类,那么我就可以修改它。

所以我的问题是:如果它是一个结构,我怎么能修改它呢?

到目前为止,我的解决方案是将当前矢量设置为新矢量

例如

(如果我只想修改x值):

me.myVector = new vector(200,me.myVector.y);

3 个答案:

答案 0 :(得分: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);

答案 1 :(得分:0)

虽然之前的说法是正确的 - 你应该使你的结构不可变 - 默认情况下它们不是这样。您可以在您拥有的设置中改变结构。

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

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

me.myVector

你得到的是struct的值的副本,而不是对它的引用。然后,尝试在同一行中修改该结构上的值是没有意义的。

以下代码可能会让它更清晰:

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

答案 2 :(得分:0)

结构通常应代表以下两种情况之一:

  • 单个统一实体,例如小数或时刻(例如DecimalDate)。

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

当应用于第一种类型的东西时,微软的指南是好的,但是编写它们的人没有考虑到一些有用的概念比第一种模式更适合第二种模式。根据事物的声音,您的数据类型适合第二种模式。因此,它应该将其整个状态暴露为公共字段(您可以这样做)。此外,在许多情况下,它应该在实际时作为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);
}

希望例如的来电者将X添加到myVector可以写

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

如果来电者有一个名为vector的{​​{1}},并且想要将一个与velocity垂直的向量添加到something.myVector那么可以:

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

请注意,在开放字段结构中使用ref参数可能非常有效,因为ref传递结构所需的时间与其大小无关。遗憾的是,没有任何Framework集合内置任何此类工具,并且C#对于使这些代码方便起作用并不大。然而,即使使用temp变量的方法也比使用所谓的“不可变”结构或不可变类的代码更清晰,更有效。