Предположим, у меня есть структура Point с общедоступными 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);
Один комментарий с точки зрения дизайна: если у вас будет конструктор, который принимает значения, которые используются для установки свойств (или полей в данном случае), для клиентов вашего класса / структуры полезно сделать аргумент конструктора имена совпадают с именами свойств, чтобы они понимали, что они устанавливают. Например: public Point(int x, int y) { ... }
Спасибо всем! Я согласился закрыть это как дубликат. В конечном итоге я просто заменил struct на class, и теперь это ссылочный тип, который хранится в куче, а не тип значения, хранящийся в стеке: проблема решена. Еще раз спасибо!
@RufusL, спасибо, Руфус, я согласен с re: design. На самом деле это не похоже на мою настоящую структуру, это просто MWE.
Ах, круто. Потому что я также собирался прокомментировать, что уже есть довольно надежный Point Structure, доступный в пространстве имен System.Drawing (исходный код для него - здесь)
@RufusL Я этого не знал! Спасибо, что поделился! Моя фактическая структура (теперь класс) хранит данные об установщиках для программы, которая устанавливает кучу программного обеспечения.





Значение вашего словаря - это объект 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());
Есть пара вещей, которые не так с тем, что вы пытаетесь сделать.
В 99,999999999% случаев структуры должны быть доступны только для чтения. См. Рекомендации Microsoft по дизайну https://docs.microsoft.com/en-us/dotnet/standard/design-guidelines/struct
X DO NOT define mutable value types.
Mutable value types have several problems. For example, when a property getter returns a value type, the caller receives a copy. Because the copy is created implicitly, developers might not be aware that they are mutating the copy, and not the original value. Also, some languages (dynamic languages, in particular) have problems using mutable value types because even local variables, when dereferenced, cause a copy to be made.
Вы также не должны изменять объект, который используется как ключ словаря. Словари основаны на хэш-значениях, и если вы измените поле ключа, вы измените его хеш-значение. См .: https://stackoverflow.com/a/3007334/4415493
The Dictionary type makes no attempt to protect against the user modifying the key used. It is purely left up to the developer to be responsible in not mutating the key.
If you think about this a bit this is really the only sane route that Dictionary can take. Consider the implication of doing an operation like a memberwise clone on the object. In order to be thorough you'd need to do a deep clone because it will be possible for an object referenced in the key to also be mutated and hence affect the hash code. So now every key used in the table has it's full object graph cloned in order to protect against mutation. This would be both buggy and possibly a very expensive operation.
Если я что-то не пропустил, OP не изменяет ключ словаря, не так ли?
Нет, но OP пытается изменить элемент, хранящийся под этим ключом - и там он получает копию
@HansKesting Верно, но половина этого ответа касается того, чтобы не изменять объект, используемый в качестве ключа ...
Первая часть имеет смысл (+1), но я не пытаюсь изменить ключ.
Я думаю, что мой вопрос следует закрыть как дубликат, но я собираюсь принять это сейчас, потому что часть меня с ОКР хочет, чтобы 1991 баллов превратился в 2000: P - хотя спасибо за помощь!
См.: stackoverflow.com/questions/441309/why-are-mutable-structs-e vil