2012-04-10 3 views
5

Обычно я выбираю между struct и классом не из-за проблем с памятью, а из-за семантики этого типа. Некоторые из моих типов значений имеют довольно большой объем памяти, иногда слишком большой, чтобы копировать эти данные все время. Поэтому мне интересно, стоит ли передавать неизменяемым значения объектов всегда по ссылке? Поскольку объекты неизменяемы, они не могут быть изменены с помощью методов, которые принимают их по ссылке. Существуют ли другие вопросы при передаче по ссылке?Передача неизменяемых типов значений по ссылке

+4

Возможно, вам нужно переосмыслить свое обоснование для решения между структурами и классами. –

ответ

14

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

памяти Это предполагает, что они не должны быть типы значений, с точки зрения реализации. Из «Руководства по проектированию для развивающихся библиотек классов», раздел "Choosing Between Classes And Structures":

Не определить структуру, если тип не имеет все следующие характеристики:

  • Это логически представляет собой одно значение, подобно примитивным типы (целое, двойное и т. д.).
  • У него размер экземпляра меньше 16 байт.
  • Неизменяется.
  • Его не нужно часто вставлять в коробки.

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

«Неизменность» для типов значений этого понятия немного жидкости - и это, конечно, не означает, что с помощью ref безопасно:

// int is immutable, right? 
int x = 5; 
Foo(ref x); 
Console.WriteLine(x); // Eek, prints 6... 
... 
void Foo(ref int y) 
{ 
    y = 6; 
} 

Мы не меняем одна части стоимости - мы заменяем все значение x совершенно другим значением.

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

+0

«У него размер экземпляра меньше 16 байт». Вы действительно пишете 'class Vector3d', потому что он имеет 24 байта? – hansmaad

+2

@ hansmaad Это просто руководство. Каждый случай не может быть охвачен им, но они должны учитывать вещи/правила при принятии решения. – vcsjones

+3

@hansmaad: Если тип, вероятно, будет * передан значением * много, то * да *, вы должны. Если тип значения вряд ли передается по значению - если вы просто создаете его и используете его локально, не переходя по другому методу, - тогда идите вперед и сделайте его большим, если хотите. –

3

Так что, интересно, стоит ли передавать неизменяемые объекты значения всегда по ссылке? Поскольку объекты неизменяемы, они не могут быть изменены с помощью методов, которые принимают их по ссылке. Существуют ли другие вопросы при передаче по ссылке?

Непонятно, что вы имеете в виду. Предполагая, что вы имеете в виду передать его как параметр ref или out, тогда метод может просто назначить новый экземпляр хранилищу. Это изменит то, что видит вызывающий, поскольку место хранения в вызываемом абоненте является псевдонимом для места хранения, переданного вызывающим абонентом.

Если вы имеете дело с проблемами памяти из-за копирования экземпляров struct, вам следует подумать о создании неизменяемого ссылочного типа, такого как string.

11

ответ Джона, конечно, правильный; Я бы добавил к этому: значения типов уже переданы по ссылке при вызове метода по типу значения. Например:

struct S 
{ 
    int x; 
    public S(int x) { this.x = x; } 
    public void M() { Console.WriteLine(this.x); } 
} 

Метод M() логически то же самое:

public static void M(ref S _this) { Console.WriteLine(_this.x); } 

Всякий раз, когда вы вызываете метод экземпляра на структуры, мы передаем реф к переменной что был приемником вызова.

Так что, если приемник не является переменной? Затем значение копируется во временную переменную, которая используется в качестве приемника. И если ценность большая, это потенциально дорогостоящая копия!

Типы значений копируются по значению; поэтому они называются типами значений. Если вы не планируете быть крайне осторожным о том, чтобы найти все возможные дорогие копии и устранить их, я бы посоветовал совет по дизайну каркаса: сохраните структуры до 16 байт и передайте их по значению.

Я также хотел бы подчеркнуть, что Джон прав: передача структуры посредством ref означает передачу ссылки на переменную, а переменные могут изменять. Вот почему их называют «переменными». В C# нет «const ref», как есть на C++; даже если сам тип значения кажется «неизменным», что не означает, что переменная является неизменной. Вы можете увидеть яркий пример того, что в этой надуманной, но образовательный пример:

struct S 
{ 
    readonly int x; 
    public S(int x) { this.x = x; } 
    public void M(ref S s) 
    { 
     Console.WriteLine(this.x); 
     s = new S(this.x + 1); 
     Console.WriteLine(this.x); 
    } 
} 

Возможно ли M выписать две разные номера? Вы бы наивно считали, что структура неизменна, и поэтому x не может измениться. Но оба s и это являются переменные и переменные могут изменить:

S q = new S(1); 
q.M(ref q); 

Это печатает 1, 2, потому что this и s являются ссылки на q, и ничто не мешает q от изменения ; не только.

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

+0

Так называемые неизменяемые структуры могут быть изменены, даже если ничто внутри их кода не взаимодействует в такой мутации. Например, хотя местоположение хранилища «KeyValuePair » может быть записано атомарно (поскольку оно принимает ровно 32 бита), имея один поток 'ToString()' в таком месте хранения, а другой поток обновляет значение, которое может вызвать 'ToString ', чтобы вернуть старый' Key.ToString() ', конкатенированный с новым' Value.ToString() '. – supercat

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