2012-04-06 2 views
13

Главный вопрос заключается в том, какие последствия позволяют изменять это ключевое слово в отношении полезности и памяти; и почему это разрешено в спецификациях языка C#?Назначить это ключевое слово в C#

На другие вопросы/субчасти может быть дан ответ или нет, если вы решите это сделать. Я думал, что ответы на них помогут прояснить ответ на главный вопрос.

я наткнулся на это как ответ на What's the strangest corner case you've seen in C# or .NET?

public struct Teaser 
{ 
    public void Foo() 
    { 
     this = new Teaser(); 
    } 
} 

Я пытался обернуть вокруг головы, почему спецификации # языка C бы даже допустить. Подраздел 1. есть ли что-нибудь, что могло бы оправдать наличие ? можно изменить? Это все полезно?

Один из комментариев к этому ответу был

От CLR через C#: Причина они сделали это потому, что вы можете вызвать без параметров конструктору структуры в другой конструктор. Если вы хотите только инициализировать одно значение struct и хотите, чтобы другие значения были равны нулю/null (по умолчанию), вы можете написать public Foo (int bar) {this = new Foo(); specialVar = bar;}. Это не эффективный и не совсем обоснованный (специальныйVar назначается дважды), но просто FYI. (Вот почему в книге, я не знаю, почему мы не должны просто сделать общественный Foo (INT-бар): это())

Sub-часть 2. Я не конечно, я следую этим рассуждениям. Может кто-то уточнить, что он имел в виду? Может быть, конкретный пример того, как он будет использоваться?

EDIT (Не считайте стек или кучу основной точки в отношении выделения памяти или сбора мусора. Вместо int [] вы можете заменить это на 262144 общедоступных int) Также из моего понимания структуры создаются в стеке как в отличие от кучи, если это структура должны были иметь 1 Мб поле байтового массива инициализируется так

public int[] Mb = new int[262144]; 

к югу части 3. ли это когда-нибудь удаляется из стека, когда Foo называется? Мне кажется, что структура никогда не выходила из сферы действия, она не будет удалена из стека. У меня нет времени сегодня, чтобы создать тестовый пример, но, возможно, я буду для этого завтра.

В коде ниже

Teaser t1 = new Teaser(); 
Teaser tPlaceHolder = t1; 
t1.Foo(); 

Суб-часть 4. Являются ли t1 и tPlaceHolder занимающими одно и то же или другое адресное пространство?

Извините, что поднял 3-летний пост, но у этого действительно есть царапина.

FYI первый вопрос о stackoverflow, поэтому, если у меня что-то не так с вопросом, добро пожаловать в комментарий, и я отредактирую.

Через 2 дня я положу щедрость по 50 на этот вопрос, даже если у меня есть победитель, выбранный в моем сознании, поскольку, как я думаю, для ответа на этот вопрос потребуется разумная работа.

+3

4 вопроса в 1 не подходят для переполнения стека. Один вопрос * должен быть одним вопросом. * –

+4

@ AnthonyPegram. В этом случае я считаю его приемлемым, так как эти вопросы являются подзапросами по отношению к основному вопросу, как работают структуры. – Polity

+1

Мои извинения не уверены, как переписать все это в одном. Главный вопрос, который, я полагаю, можно суммировать, каковы побочные эффекты, позволяющие изменить это ключевое слово? Вопросы 3-4 являются побочными эффектами, которые, как я думал, могут быть возможны. Если кто-то имеет право редактировать вопрос, кроме меня, и думает, что они могут попросить его лучше, пожалуйста, не стесняйтесь делать это. Или оставить комментарий, предлагающий лучший вопрос, и я начну с нуля. –

ответ

6

Прежде всего, я думаю, вы должны начать с изучения, если вы даже задаете правильный вопрос. Возможно, мы должны спросить: «Почему C# не разрешить назначение this в структуре?"

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

Назначение ключевому слову this в типе значения, однако , является корректным. Назначение типов значений - операция копирования. Значение каждого поля рекурсивно копируется с правой стороны на левую часть задания. Это абсолютно безопасная операция над структурой, даже в конструкторе, потому что e оригинальная копия структуры все еще присутствует, вы просто меняете свои данные. Это точно эквивалентно ручному настройке каждого поля в структуре. Почему спецификатор или компилятор запрещают корректную и безопасную операцию?

Это, кстати, отвечает на один из ваших вопросов. Назначение типа значения - операция глубокой копии, а не копия ссылки. Учитывая этот код:

Teaser t1 = new Teaser(); 
Teaser tPlaceHolder = t1; 
t1.Foo(); 

Вы выделено две копии Teaser структуры и скопированное значение полей в первом в поле во втором. Это характер типов значений: два типа, которые имеют одинаковые поля, идентичны, как и две переменные int, которые содержат 10 одинаковых, независимо от того, где они находятся «в памяти».

Кроме того, это важно и стоит повторять: тщательно делайте предположения о том, что происходит с «стеком» и «кучей». Типы значений заканчиваются в куче все время, в зависимости от контекста, в котором они используются. Краткоживущие (локально ограниченные) структуры, которые не закрыты или иным образом сняты из сферы действия, скорее всего, будут распределены в стеке. Но это неважно implementation detail что вы не должны ни заботиться, ни полагаться. Ключ в том, что они являются типами значений и ведут себя как таковые.

Насколько полезное назначение this действительно: не очень. Уже упоминались конкретные случаи использования. Вы можете использовать его для большей части инициализации структуры со значениями по умолчанию, но указать небольшое число. Так как вы должны установить все поля, прежде чем ваш конструктор возвращает, это может сэкономить много избыточного код:

public struct Foo 
{ 
    // Fields etc here. 

    public Foo(int a) 
    { 
    this = new Foo(); 
    this.a = a; 
    } 
} 

Он также может быть использован для выполнения быстрых операций свопа:

public void SwapValues(MyStruct other) 
{ 
    var temp = other; 
    other = this; 
    this = temp; 
} 

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

+0

+1 для попытки до его закрытия. Я постараюсь повторить этот вопрос завтра. –

+0

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

1

Наличие этого присваиваемого допускает «расширенные» угловые случаи с structs. Одним из примеров я нашел был метод замены:

struct Foo 
{ 
    void Swap(ref Foo other) 
    { 
     Foo temp = this; 
     this = other; 
     other = temp; 
    } 
} 

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

Теперь, когда речь идет о структурах самих себя. Они отличаются от классов несколькими способами:

  • Они могут жить на стеке, а не на управляемой куче.
  • Их можно перевести на неуправляемый код.
  • Им нельзя присвоить значение NULL.

Полный обзор см: http://www.jaggersoft.com/pubs/StructsVsClasses.htm

Относительно вашего вопроса, живет ли ваша структура в стеке или в куче. Это определяется расположением расположения структуры. Если структура является членом класса, она будет выделена в куче. Иначе, если структура будет распределена напрямую, она будет выделена в куче (на самом деле это только часть изображения. Все это станет довольно сложным, когда начнется разговор о закрытии, введенном в C# 2.0, но пока этого достаточно для того, чтобы ответьте на свой вопрос).

Массив в .NET по умолчанию распределяется по куче (это поведение несовместимо при использовании небезопасного кода и ключевого слова stackalloc). Возвращаясь к объяснению выше, это указывает на то, что экземпляры struct также распределяются по куче. Фактически, простой способ доказать это - выделить массив размером в 1 мб и наблюдать, как генерируется исключение NO stackoverflow.

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

Структура can not имеет ссылки на нее (указатели возможны в неуправляемом коде). При работе с структурами в стеке на C# у вас в основном есть метка для экземпляра, а не для ссылки. Присвоение одной структуры другому просто копирует базовые данные. Вы можете видеть ссылки как структуры. В наивном порядке ссылка - это не что иное, как структура, содержащая указатель на определенную часть в памяти. При назначении одной ссылки на другую данные указателя копируются.

// declare 2 references to instances on the managed heap 
var c1 = new MyClass(); 
var c2 = new MyClass(); 

// declare 2 labels to instances on the stack 
var s1 = new MyStruct(); 
var s2 = new MyStruct(); 

c1 = c2; // copies the reference data which is the pointer internally, c1 and c2 both point to the same instance 
s1 = s2; // copies the data which is the struct internally, c1 and c2 both point to their own instance with the same data 
+0

Я думаю, что независимо от того, что оно скопировано в стек или в куча при вызове Foo будет GC в конечном итоге очистить память, связанную с исходной структурой? Просто для разъяснения я говорю о строго управляемом коде. Ohh и Foo Я имел в виду Foo в структуре Teaser, а не вашу структуру Foo. –

+0

GC очищает только экземпляры, выделенные на управляемой куче. Установки на основе стека фактически не очищены (следовательно, структура can not содержит деструктор). Их ярлык просто станет недействительным после выхода из сферы действия. См. Ответ eric lippert в этом сообщении: http://stackoverflow.com/questions/6441218/can-a-local-variables-memory-be-accessed-outside-its-scope – Polity

+0

structs - типы значений; они никогда явно не собираются мусором. Память для типов значений возвращается ОС, когда тип значения оставляет область; если это локальная переменная или параметр, это происходит, тогда функция возвращается. Если это поле, это происходит, когда его контейнерный класс GC'd. И т. Д. –

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