修改struct变量

时间:2018-05-18 20:20:42

标签: c#

假设我有一个带有public int的x和y的struct Point。我想改变一个值,我可以。但是一旦我将它存储在字典中,我就不能再这样做了。为什么呢?

MWE:

using System;
using System.Collections.Generic;

public class Program
{
    public struct Point
    {
        public int x, y;

        public Point(int p1, int p2)
        {
            x = p1;
            y = p2;
        }
    }

    public static void PrintPoint(Point p)
    {
        Console.WriteLine(String.Format("{{x: {0}, y: {1}}}", p.x, p.y));
    }

    public static void Main()
    {
        var c = new Point(5, 6);
        PrintPoint(c);                                          // {x: 5, y: 6}

        c.x = 4;  // this works
        PrintPoint(c);                                          // {x: 4, y: 6}

        var d = new Dictionary<string, Point>() 
        {
            { "a", new Point(10, 20) },
            { "b", new Point(30, 40) }
        };

        foreach (Point p in d.Values)
        {
            PrintPoint(p);  // this works
        }

        PrintPoint(d["a"]); // this works                       // {x: 10, y: 20}

        Console.WriteLine(d["a"].x.ToString());  // this works  // 10

        // d["a"].x = 2; // why doesn't this work?
    }
}

为什么我可以在字典中访问结构变量但不能再更改它们?我该如何改变它们?

2 个答案:

答案 0 :(得分:2)

您尝试做的事情有几个问题。

在99.999999999%的情况下,结构应该是只读的。请参阅Microsoft设计指南https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/struct

  

X不要定义可变值类型。

     

可变值类型有几个问题。例如,当一个   property getter返回一个值类型,调用者接收一个副本。   由于副本是隐式创建的,因此开发人员可能不知道   他们正在改变副本,而不是原始值。也,   某些语言(特别是动态语言)使用时遇到问题   可变值类型,因为即使是局部变量,当取消引用时,   导致复制。

您也不应该改变正在用作字典键的对象。字典基于哈希值,如果更改键的字段,则将更改其哈希值。请参阅:https://stackoverflow.com/a/3007334/4415493

  

字典类型不会尝试防范   用户修改使用的密钥。它纯粹由开发人员负责   负责不改变密钥。

     

如果你想到这一点,这真的是唯一理智的路线   字典可以采取。考虑做一个的含义   像对象上的成员克隆一样操作。为了   彻底的你需要做一个深刻的克隆,因为它是可能的   密钥中引用的对象也会发生变异,从而影响   哈希码。所以现在表中使用的每个键都有它的完整对象   克隆图以防止突变。这将是两者   越野车,可能是非常昂贵的操作。

答案 1 :(得分:1)

您的字典值是Point对象。因此,您可以使用新的Point对象更新密钥。

Right Way (Update):
            d["a"]= new Point(5, 20);
            PrintPoint(d["a"]);
            Console.WriteLine(d["a"].x.ToString());

Round About: (Remove and Add)
            d.Remove("a");
            d.Add("a", new Point(2, 20));
            PrintPoint(d["a"]);
            Console.WriteLine(d["a"].x.ToString());