Изменить структурную переменную

Предположим, у меня есть структура 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"] возвращает временную копию, которую вы нигде не храните. Модификация временного не имеет смысла

UnholySheep 18.05.2018 22:23

Он «не работает», потому что компилятор жалуется. Текст жалобы актуален.

Hans Kesting 18.05.2018 22:49

Один из способов решить эту проблему - назначить новый Point с новым значением x и тем же значением y элементу словаря: d["a"] = new Point(2, d["a"].y);

Rufus L 18.05.2018 23:01

Один комментарий с точки зрения дизайна: если у вас будет конструктор, который принимает значения, которые используются для установки свойств (или полей в данном случае), для клиентов вашего класса / структуры полезно сделать аргумент конструктора имена совпадают с именами свойств, чтобы они понимали, что они устанавливают. Например: public Point(int x, int y) { ... }

Rufus L 18.05.2018 23:03

Спасибо всем! Я согласился закрыть это как дубликат. В конечном итоге я просто заменил struct на class, и теперь это ссылочный тип, который хранится в куче, а не тип значения, хранящийся в стеке: проблема решена. Еще раз спасибо!

Arthur Dent 18.05.2018 23:03

@RufusL, спасибо, Руфус, я согласен с re: design. На самом деле это не похоже на мою настоящую структуру, это просто MWE.

Arthur Dent 18.05.2018 23:04

Ах, круто. Потому что я также собирался прокомментировать, что уже есть довольно надежный Point Structure, доступный в пространстве имен System.Drawing (исходный код для него - здесь)

Rufus L 18.05.2018 23:07

@RufusL Я этого не знал! Спасибо, что поделился! Моя фактическая структура (теперь класс) хранит данные об установщиках для программы, которая устанавливает кучу программного обеспечения.

Arthur Dent 18.05.2018 23:35
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
9
1 555
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Значение вашего словаря - это объект 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 не изменяет ключ словаря, не так ли?

Rufus L 18.05.2018 22:46

Нет, но OP пытается изменить элемент, хранящийся под этим ключом - и там он получает копию

Hans Kesting 18.05.2018 22:50

@HansKesting Верно, но половина этого ответа касается того, чтобы не изменять объект, используемый в качестве ключа ...

Rufus L 18.05.2018 22:53

Первая часть имеет смысл (+1), но я не пытаюсь изменить ключ.

Arthur Dent 18.05.2018 22:55

Я думаю, что мой вопрос следует закрыть как дубликат, но я собираюсь принять это сейчас, потому что часть меня с ОКР хочет, чтобы 1991 баллов превратился в 2000: P - хотя спасибо за помощь!

Arthur Dent 18.05.2018 22:58

Другие вопросы по теме