【问题标题】:Modify struct variable [duplicate]修改结构变量[重复]
【发布时间】:2018-10-29 07:59:01
【问题描述】:

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

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?
    }
}

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

【问题讨论】:

  • 因为d["a"] 返回一个您没有存储在任何地方的临时副本。修改一个临时的没有意义
  • 它“不起作用”,因为编译器会抱怨。该投诉的内容是相关的。
  • 解决此问题的一种方法是为字典项分配一个新的Point 和新的x 值和相同的y 值:d["a"] = new Point(2, d["a"].y);
  • 从设计的角度来看一个评论:如果您将拥有一个构造函数,该构造函数接受用于设置属性(或本例中的字段)的值,这对您的客户很有帮助class/struct 使构造函数参数名称与属性名称相同,以便他们了解他们正在设置的内容。例如:public Point(int x, int y) { ... }

标签: c#


【解决方案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()); 

【讨论】:

    【解决方案2】:

    您正在尝试做的事情有几处错误。

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

    X 不定义可变值类型。

    可变值类型有几个问题。例如,当一个 属性 getter 返回一个值类型,调用者收到一份副本。 因为副本是隐式创建的,开发人员可能不知道 他们正在改变副本,而不是原始值。还, 某些语言(尤其是动态语言)在使用时存在问题 可变值类型,因为即使是局部变量,在取消引用时, 制作副本。

    您也不应该改变用作字典键的对象。字典是基于散列值的,如果你改变了键的一个字段,你就会改变它的散列值。见:https://stackoverflow.com/a/3007334/4415493

    Dictionary 类型不会尝试防止 用户修改所使用的密钥。它完全由开发人员决定 负责不改变密钥。

    如果您仔细考虑一下,这确实是唯一合理的方法 字典可以带。考虑做一个 操作类似于对象上的成员克隆。为了 彻底你需要做一个深度克隆,因为它可能 键中引用的对象也将发生变异并因此影响 哈希码。所以现在表中使用的每个键都有它的完整对象 克隆图以防止突变。这将是两者 越野车,可能是一项非常昂贵的操作。

    【讨论】:

    • 除非我错过了什么,否则 OP 不会改变字典键,是吗?
    • 不,但是 OP 正在尝试更改存储在该键下的项目 - 并且在那里他得到了一份副本
    • @HansKesting 对,但这个答案的一半是关于不改变用作键的对象......
    • 第一部分是有道理的(+1),但我不想更改密钥。
    • 我认为我的问题应该作为重复问题关闭,但我现在要接受这个问题,因为我的强迫症部分希望看到 1991 点变为 2,000 :P - 感谢您的真实帮助不过!
    猜你喜欢
    • 2023-03-19
    • 2017-12-10
    • 1970-01-01
    • 2015-10-01
    • 2011-09-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多