Pull to refresh

Comments 61

Вывод: Это не структуры плохие, это их неправильно применяют. Название топика замануха?
Конечно. Надо четко понимать, для его нужны структуры и не пытаться использовать их по-другому. Често говоря, я был бы не против, чтобы структуры (по крайней мере, пользовательские) можно было бы использовать только в unchecked коде.
Может быть, вы имели в виду unsafe, а не unchecked? Причем здесь проверка чисел на переполнение…
Структуры — это абсолютно нормально, особенно если надо связать C/C++ c C#. Просто надо понимать разницу между типами значений и типами ссылками.
Мне лично кажется, что структуры — пережиток неуправляемого прошлого (как вы и заметили).
они полезны и вне контекста неуправляемого кода. Ну к примеру ваш метод должен вернуть тикет в котором 2 ключа типа GUID. У вас только 2 варианта.
1) протащить через аргументы(out/ref или объект куда сложить), но это право кривое решение
2) вернуть структуру, тем самым отложив пробуждение GC(а это страшно, господа)
Можно вернуть класс. Можно и структуру, конечно, именно тут и работает ремарка про здравый смысл.
Нельзя вернуть класс, иначе GC проснется раньше времени и всем процессам в системе не поздоровится.
Класс попадет в кучу. Учитывая что тикеты это вещь расходная, то каждый тикет будет приближать время первой сборки мусора.
Не будьте столь категоричными! Смотрите мой пост ниже!
Или вот еще одно применение из реального мира.
В Managed DirectX, XNA объекты типа Matrix, Vector, Point являются структурами. Полагаю будь они by-ref, то пришлось бы очень много раз их копировать и абандомить.

Так происходит с Matrix в System.Drawing

Это примитивные типы, они не должны храниться в куче. Только в стеке или объекте.
И ещё такой пример

for (int i = 0; i
Здесь, я думаю, структуры нужны именно для unmanaged перехода в directX (хотя, конечно, могу и ошибаться)
они by design должны передаваться по значению, иначе их поведение становится неэффективным с точки зрения памяти и сборки мусора. Все кроме Matrix не покидают пределов среды исполнения, и не используются для связи с unmanaged кодом.

Кто сейчас вспомнит про заполнение Vertex буфера векторами и флотами, то это не передача в unmanaged, а сериализация.
Есть неуправляемое настоящее и неуправляемое будущее, так как работать например вплотную с шиной или процессором из управляемого кода просто не возможно.
P.S.: Возможности есть, но они сильно вырываются из контекста языка, как правило, или очень громоздкие.
Господин Sane, настоятельно рекомендую ознакомится с книгой CLR via C# Джефрри Рихтера, дабы понять для чего существуют структуры в .Net и в каких местах они дают преимущество!
Честно говоря, не увидел у Рихтера ничего, чему бы я противоречил в посте.
Да, не противоречит. Ладно, пойдём от противного: когда лучше всего использовать структуры?
Это можно прочитать у Рихтера: The type acts as a primitive type, Instances of the type are small (approximately 16 bytes or less), Instances of the type are large (greater than 16 bytes) and are not passed as method parameters or returned from methods. К последнему я бы добавил все-таки пару слов про массивы.
и не только! Господин Olostan ниже уже написал ответ, который я имел в виду. Это подробно описано у Рихтера, Вы книгу невнимательно читали!
Тогда будьте любезны приведите цитаты.
используйте IEnumerable или в крайнем случае IList (но только как ReadOnlyCollection)
Такие типы данных неизменяемые?
Они по крайней мере позволяют избегать описанной в третьем пункте проблемы.
Каким образом? Если вы напишете так?

struct Foo
{
public int Field;
public IEnumerable Bars;
}
После этого вы не сможете сделать foo.Bar[0], а изменениа самого поля foo.Bar приведет к изменению только в локальной копии.
Т.е. если вместо Bar[] написать Enumerable<Bar>?
int, long и всё остальное — зло. Так и запишем.
они by-value, но не структуры. Они примитивы

В C# это синонимы, а вообще нет.

Чойрт, это топик знаний и добра.
Иимхо основная польза от структур — жесткий контроль над циклом жизни: создали в стеке, вышли из метода — и нет структуры. garbage collector отдыхает.

При умелом использовании структур можно получить громадную прибыль по производительности, если используется много мелких container-like сущностей.

А использовать как immutable… увольте.
Жесткий контроль над временем жизни достигается с помощью using — для чего он и был придуман. Использовать структуры только для этого — забивать гвозди микроскопом.
жизненный цикл, о котором вы только что сказали, не есть время жизни.

После Dispose объект живет еще долго. Для справки using это try {… } finally { obj.Dispose() }
В управляемом мире, четсно говоря, время жизни объекта уже не важно. В принципе, оно так же не важно в неуправляемом, но там концепция «время жизни» хорошо совпало с «предопределнным поведением» в виде неявного вызова деструктора. В дотнет эти два понятия разделили и добавили возможность все-таки их совместить с помощью финалайзеров.
как уже заметили — using — это скорее «мягкий» контроль. Сборщик мусорка как захочет, так и освободит.

А вот с помощью структур ты точно знаешь, что контейнер (прошу заметить, я не называю это классом или объектом — именно сущность, которая содержит в себе некоторую информацию) будет освобожденна именно в тот момент, когда метод завершит свою работу.

Именно по этому и есть boxing — когда тебе надо продлить время жизни сущности за пределы вызова метода происходит boxing и сущность попадает в хип, и дальше идет уже игра с отслеживанием ссылок на объект в хипе. Когда не надо — сущность живет в стеке, и пропадает после выхода.

Сразу отвечу на следующий коммент про «время жизни не важно» — согласен. Но хорошее понимание времени жизни данных дает возможность более тонкой настройки производительности.

И ее нужно делать только после того, как профайлинг показал что именно это является «узким горлышком» — например наличие в периоды упада производительности большого количества мелких объектов. Но не в коем случае на уровне дизайна, так как структуры действительно добавляют довольно серьезные (тем, что мало-заметные) подводные камни, описанные в топике.
К вопросу об immutability: вон Андерс Хельсберг в своих интервью постоянно намекает на то что в C# недостаточно конструктов для описания immutability. Значит ли это что мы увидим public readonly class MyClass или похожие декларации? На самом деле с этим море проблем – даже тот же F# позволяет менять элементы массива при том что все, в принципе, immutable.
F# позволяет менять элементы массива — потому что массив по определению мутабельная структура данных.
Да, именно этому и посвящен второй вывод.
Я всегда был уверен, что структуры хранятся в стеке, то есть куча и сборщик мусора тут ни при чем. Так что абзац там про Large Object Heap меня как-то удивил.
Структуры — да, массивы — нет, это рефернс-тайп.
Они также могут храниться и в куче — например когда являются полями референс типа
Наверное, если бы структуры в принципе были бы плохими, их бы в .NET не включили. Так что плохи не структуры, а изменяемые структуры, потому, что просто написать код, который делает не то, что хочет разработчик. Эрик Липперт упоминает об этом при первой удобной возможности.
Выводы статьи совершенно некорректны.
1. Все классы структур нужно делать неизменяемыми (immutable).
2. Структуры нужно использовать тогда, когда это удобно.
3. Структуры, передаваемые в методы по ссылке (ключевые слова ref и out) передаются, кто бы мог подумать — по ссылке, а не по значению — копирования не происходит, производительность не страдает.
4. Необходимо соблюдать инкапсуляцию. Если метод класса возвращает некоторый объект, модификация которого затронет состояние экземпляра класса, то класс должен позаботится о предотвращении такой модификации. Например, если требуется вернуть внутренний массив, нужно вернуть Array.AsReadOnly(source) а не сам source.
Это же всё прописные истины ООП, которые верны для любого объектно-ориентированного языка верхнего уровня.
Спасибо. Подпишусь под каждым словом.
а поясните 2 — когда удобно?
Ровно в тех же ситуациях, что и в любом языке высокого уровня с поддержкой структур по значению. На C++ или на Pascal/Delphi писали?
э… я конечно не волшебник, я только учусь, но в книжке написано что структуры стоит использовать для выигрыша в скорости, жертвуя чистотой ООП.

точнее там написано что структуры не стоит использовать без особой необходимости.

не совсем понял каким образом структура защищает данные если, опять же в книжке, написано что они почти ничем не отличаются от классов кроме отсутствия наследования (?) и места хранения ну и ссылочного типа вызова. Вызов экземпляра классе вроде бы тоже может быть оформлен в виде ссылки (?)

В общем вы меня ещё больше запутали.
Структура «защищает» данные тем, что по умолчанию передается по значению — значит метод получит новую копию структуры и изменения в копии не затронет оригинальную структуру — смотрите первый пример кода.
"… любые методы получают только копию и не в силах ничего подменить"
эээ… а использование волшебного слова ref?
Волшебное слово ref является явным соглшанием по изменению, как и для классов.
Что значит «явное соглашение по изменению, как и для классов»?
Я имел ввиду, что слово «ref» является фвным указанием того, что передается именно ссылка на объект и изменения, внесенные в методе, будут применены и вне метода.
Только запутали еще больше после этой статьи, так и не указав конкретно где использовать структуры.
Sign up to leave a comment.

Articles