2017-01-23 6 views
0

Я много читал о изменчивости структур в C#. Большинство слов говорят, что изменчивые структуры являются злыми. Я понимаю, почему. Но у меня есть случай, когда изменчивая структура кажется законной. Конечно, я сталкиваюсь с проблемами, которые распространяются вокруг структурной изменчивости.юридический (?) Изменяемый случай структуры в C#

Скажет, у меня есть, простая структура, как это, в приложении редактора ГИСА:

enum CoordinateSystem : byte { .... } // coordinate system types 

struct Vertex 
{ 
    float m_x; // m_ is the original value of coordinate, it needs to be kept 
    float m_y; 
    float c_x; // c_ is a cache of the result of expensive coordinate transformation 
    float c_y; 
    CoordinateSystem cachedSystem; // the current coordinate system type in c_x and c_y 
    public float x { get {...} set {...}} // properties are for accessing values outside of struct 
    public float y { get {...} set {...}} // and transforming m_x,m_y into c_x,c_y if necessary 
} 

Свойство для доступа к координате снаружи. Из внешнего одноэлементного класса известно, какая структура системы необходима при доступе к свойствам x и y. Свойство get преобразует m_x и m_y по требованию, если необходимо, и помимо возвращаемого значения он сохраняет результат в c_x и c_y для дальнейшего доступа. Конечно, кеширование необходимо, потому что преобразование координат очень дорого. При кэшировании только первый доступ будет медленным, и все дальнейший доступ в одной и той же системе координат будет «немедленным».

Вершины живут в списке, поэтому в списке вершин длиной 10000 выделяется вершина [10000] в списке.

Очевидно, проблема заключается в том, что доступ к вершине из списка возвращает копию структуры, хранящейся в списке <>, поэтому кэширование после преобразования не влияет, поскольку оно сохраняет результат в короткоживущей копии , а не в списке <> сам.

Я не хочу менять Вершину в класс, потому что она будет выделять (в 64-битной версии) 10000 * 8 байтов в качестве ссылки в одном блоке и 10000 * 17 байт в блоке 10000, это примерно равно 1/3 памяти и 10000 раз количество выделенных блоков. (Я знаю, что это грубый расчет из-за выравнивания, ...) И это примерно один список вершин, у меня их десять тысяч, поэтому класс будет тратить много памяти.

Я бы хотел, чтобы преобразование было прозрачным для внешних аксессуаров, потому что Vertex - это основной тип приложения, на который ссылается много раз. Другая причина заключается в том, что только координаты вершин знают о координатных системах, все остальные части смотрят на Vertex.x и Vertex.y как «числа», не более того. Таким образом, свойства x и y должны выполнять преобразование.

Вопрос: какой правильный подход для решения этой проблемы?

(Пожалуйста, не закрывайте этот вопрос как на основе мнений. Это не один из этого. Я заинтересован в ответах, основанных на фактах, принимая во внимание цели применения.)

+0

Не можете ли вы обновить список с измененной копией? –

+0

Я думаю, что лучше отделить неизменяемые и изменяемые части, иметь отдельный тип для фактических значений и кешированных значений. Я не думаю, что 'CoordinateSystem' может отличаться для каждой вершины. поэтому лучше иметь кэшированные значения в другом списке, и когда когда-либо изменения 'CoordinateSystem' воссоздают список с новыми значениями. –

+1

Такая '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' используется только при сохранении/экспонировании из массивов, которые в настоящее время являются единственной структурой коллекции, C# 7 'ref возвращает', вероятно, позволит подвергать большее количество индексов, таких как массив, но до тех пор ... Кроме того, для обратной совместимости они, вероятно, будут хранить« Список », как сейчас. –

ответ

0

Как рассказал Иван Стоев us ", которые в настоящее время являются единственной структурой коллекции, которая позволяет мутировать элемент структуры с помощью индексатора". И список <> использует массив.

Итак, я выбираю грязное, предосудительное, нецелевое и злобное решение, я предвижу downvotes ... но у меня должно было быть решение, которое подходит для гигабайт данных в памяти и не требует слишком большого количества кода.

Я изменил индексатор класса <>, чтобы мутировать (пересчитать) часть кэша структуры, которая живет в массиве хранения при доступе к элементу списка.

К сожалению, массив хранения списка <> является закрытым, не защищенным, его нельзя получить через наследование.Поэтому я пошел дальше на более демоническом пути: из sourcesource я скопировал необходимые части исходного кода List <> и (с другим именем, конечно), наконец, у меня появился новый класс со списком <> функциональность, но с возможностью обновления содержимое для доступа к индексу.

Опять же, это решение, за которым никогда не следует следовать никто. Но это хорошо работает ...

+1

Я уже давно думал, что 'List ' и 'IList ' должен был включить метод 'AccessElement (int index, ActionByValueRefRef proc, ref U extraValue)', который будет вызывать 'proc (index, ref listItem [index] , ref extraValue) '[вдоль, возможно, с формой, которая опускает' extraValue']. Такая функция позволила бы коду воспользоваться многими преимуществами доступа к базовому массиву, но не требовав, чтобы сбор предоставлял его массив (или даже использовал его). – supercat

Смежные вопросы