Подтвердить что ты не робот

Назначьте это ключевое слово в С#

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

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

Я столкнулся с этим как ответ на Какой самый странный угловой случай, который вы видели на С# или .NET?

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

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

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

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

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

РЕДАКТИРОВАТЬ (игнорировать стек или кучу основной точки в отношении выделения памяти или сбора мусора. Вместо 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 по этому вопросу, даже если у меня есть победитель, выбранный в моем сознании уже, поскольку я думаю, что ответ потребует разумной работы для объяснения вопросов.

4b9b3361

Ответ 1

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

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

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

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

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

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

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

Насколько полезным назначение 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;
}

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

Ответ 2

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

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

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

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

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

Полный обзор приведен ниже: http://www.jaggersoft.com/pubs/StructsVsClasses.htm

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

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

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

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

// 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