无法修改结构体成员

14

我并不是编程新手,但是在理解C#结构体方面似乎有些薄弱。

有人能解释一下为什么以下代码输出如下结果吗?

Dist1: 0, Dist2: 0

struct Distance
{
    public void SetFeet(int feet) { Value = feet; }
    public void SetMiles(float miles) { Value = (int)(miles * 5280f); }
    public int GetFeet() { return Value; }
    public float GetMiles() { return Value / 5280f; }
    private int Value;
}

class Distances
{
    public Distance Dist1 { get; set; }
    public Distance Dist2 { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Distances distances = new Distances();
        distances.Dist1.SetFeet(1000);
        distances.Dist2.SetFeet(2000);

        Console.WriteLine("Dist1: {0}, Dist2: {1}",
            distances.Dist1.GetMiles(),
            distances.Dist2.GetMiles());

        Console.ReadLine();
    }
}

6
这正是为什么使用可变结构体不是一个好主意的原因。 - Jeff Mercado
@Jeff:好的,但是当有多种方式解释参数时,你如何实现将值分配给此结构体的代码呢?(在我的情况下,我猜我可以分配一个 intfloat 并将它们解释为英尺和英里,但我的真实代码有更多解释底层值的方式。) - Jonathan Wood
让它变成不可变的。也就是说,不要让在你的结构体中存储的值可以被更改。查看框架中现有值类型的公共接口以及它们如何处理“更改值”。例如:DateTimeStringKeyValuePair<TKey, TValue>。如果Yahia的答案不令人满意,我可以添加一个答案。 - Jeff Mercado
3个回答

14

获取器和设置器——用于访问属性的方式——在这方面仍然像方法一样运行。

distances.Dist1.SetFeet(1000);

等价于

distances.GetDist1().SetFeet(1000);

从 getter 方法返回结构体(值)时,会生成其副本。如果 Dist1成员变量,则情况就不同了,它会按预期工作。

祝编码愉快。


最好的解释! - Sam Sch

10

结构体是值类型 - 因此,当您访问distances.Dist1.SetFeet时,您实际上在访问其副本...例如,请参见MSDN http://msdn.microsoft.com/en-us/library/aa288471%28v=vs.71%29.aspx

[在评论后编辑]
另一方面,如果您执行distances.Dist1 = new Distance().SetFeet(1000); 并将SetFeet的返回类型从void更改为Distance,则应该可以正常工作。 或者将Distance更改为类。

有关如何构建结构体使其按预期工作的参考,请参见框架中的DateTime结构体- http://msdn.microsoft.com/en-us/library/system.datetime.aspx
[在评论后编辑]


1
我知道结构体是值类型。但我不明白 distances.Dist1.SetFeet() 是如何进行复制的。复制在哪里进行?你能详细说明一下吗? - Jonathan Wood
1
刚刚添加了一个到MSDN的链接,其中包含类似于你的示例。访问distances.Dist1会给你一个值(而不是引用)...这是字段Dist1的副本 - 然后你在该副本上调用SetFeet...将结构体更改为类,立即看到它可以工作(因为你得到了一个引用)。 - Yahia
好的,我现在明白了。谢谢。看起来有点尴尬 - 我不能使用您建议的代码,因为SetFeet()没有返回可以分配给Dist1的值。 - Jonathan Wood
增加了DateTime作为结构体实现的示例,并更正了赋值(将结构体的所有“修改方法”的返回类型从void更改为Distance)。 - Yahia

-1

属性和变量的处理方式不同,只需从您的 Distance 声明中删除 { get; set; },代码就可以正常工作。

struct Distance
{
    public void SetFeet(int feet) { Value = feet; }
    public void SetMiles(float miles) { Value = (int)(miles * 5280f); }
    public int GetFeet() { return Value; }
    public float GetMiles() { return Value / 5280f; }
    private int Value;
}

class Distances
{
    public Distance Dist1;//here
    public Distance Dist2;//and here
}

class Program
{
    static void Main(string[] args)
    {
        Distances distances = new Distances();
        distances.Dist1.SetFeet(1000);
        distances.Dist2.SetFeet(2000);

        Console.WriteLine("Dist1: {0}, Dist2: {1}", distances.Dist1.GetMiles(),
            distances.Dist2.GetMiles());

        Console.ReadLine();
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接