Как обнулить переменную c

от admin

Как: сбросить переменную в C #?

Это действительно смешно, но мне не удалось найти ответ самостоятельно. Как я могу отключить переменную? Например. php имеет функцию unset ($ var).

9 ответов

На самом деле не существует эквивалента «unset».

Самое близкое совпадение, которое я знаю, это использование ключевого слова default.

Переменная по-прежнему будет «установлена», но она будет иметь значение по умолчанию.

Вы не можете. Нет понятия «отменить» переменную. Вы можете установить его на другое значение — 0, нуль, независимо от того, что необходимо. У экземпляров/статических переменных даже нет понятия о том, установлена ​​или не установлена ​​переменная, а локальные переменные имеют «определенно назначенные» или «не определенно назначенные».

Чего вы пытаетесь достичь?

Как правило, его значение равно null, задает ли задание (для переменной типов, например int, вы должны сделать ее нулевой версией int?).

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

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

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

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

Переменные типа значения не нужны. Они постоянно выделяются.

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

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

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

How to reset static variables within a function

Is there a way to reset variables declared as static within a function? The goal is to make sure that the function is not called with lingering values from an unrelated call. For example, I have a function opearting on columns of a matrix.

The function is called n times, once for each column. Is this a proper way of «re-setting» the static variable? Are there other general fool-proof ways of resetting static variables? For example, I want to make sure that if the call is made with a new matrix with possibly different dimensions then the vector v is resized and zeroed etc. It seems the easiest way may be to call the function with a NULL pointer:

5 Answers 5

Use an idempotent initializer function and global variables instead.

If your initializer is really idempotent, you can call it again to reset the variables.

If you need this to be called automagically, use __attribute__((constructor)) (for GCC) like so:

However, you should note that if you need to do this, you should reconsider using in-function static variables and instead use passed-in fresh ones that are returned/written and passed to subsequent related calls.

I’d recommend turning it into a struct and writing a small helper function for managing the semantics of what you’re trying to do. It could return the buffer if the request is appropriate for its size, or create a new one on demand (and free the old one) if necessary.

One approach I’ve seen used when a C module was imported to C++ was to surround the whole module with a class wrapper, and replace all static variables inside functions with uniquely-named «global» varaibles outside the functions. I don’t know any good way to achieve a similar effect for projects involving multiple source files, though I’d like to know if one exists. I have some embedded system code in C, which I simulate by adding some C++ wrappers in VS2005. For example, I have I/O registers defined so that something like TX1CON = 0x5C; would translate into something like IOMAP(0x251).P = 0x5C; IOMAP is a property which would send «write 0x5C to address 0x251» to a hardware-simulation program. This approach works well, but I can’t do a clean reset. Any ideas?

An approach which can sometimes be helpful if one needs a «reset» method which can hit an unknown number of functions or modules is to have a global counter for how many times that reset method has been called, and then have each function or module include code like:

In some multi-threading contexts, if the initialization of the static variables may depend upon some global variables, one may wish to replace the «if» with a «while»; in such a case; memory barriers may also be needed in such a case, though the exact requirements would vary depending upon the operating environment.

Also, an alternative pattern that may be useful within embedded systems would be to have a modules_initialized global variable which gets set to 0 by the global reset method, and then have each module start with something like:

This would require that there be no more than 32 module ID’s, and would require that they be uniquely allocated somehow, but some systems can handle that pretty nicely. For example, a linker may allow one to define a «data section» from address 0-31 of an address space independent from any other; if each module declares a single-byte variable within that address space, the linker could generate the appropriate addresses for those variables.

Как сбросить переменную в С#?

Как сбросить переменную? Например, в PHP есть unset($var) функции.

10 ответы

На самом деле нет эквивалента «неустановленному».

Самое близкое совпадение, которое я знаю, это использование по умолчанию ключевое слово.

Переменная по-прежнему будет «установлена», но будет иметь значение по умолчанию.

int[] array1 = new int[] < 1, 3, 5, 7, 9, 10 >; , а затем int array1 = default(int); возвращает >> Локальная переменная с именем ‘array1’ уже определена в этой области — ви

Обращение к приведенному выше комментарию: это потому, что вы повторно объявляете локальную переменную array1 (вы изначально объявляете его как int[] array1 , затем попытайтесь объявить его во второй раз как int array1 ). Предполагая, что вы не собирались менять тип с int[] в int (что невозможно в С#), ваше исправление должно быть: array1 = default(int[]); — рпатель

Вы не можете. Нет понятия «сбросить» переменную. Вы можете установить для него другое значение — 0, null, что угодно. Экземплярные/статические переменные даже не имеют представления о том, установлена ​​ли переменная или нет, а локальные переменные имеют только «определенно назначенные» или «неопределенно назначенные».

Чего вы пытаетесь достичь?

ответ дан 13 окт ’08, 12:10

Наиболее важным использованием unset() в PHP является сброс переменных, которые не определены в области видимости (функция или класс), IMO. Из-за этого C# не нужно сбрасывать, как это делает PHP. 🙂 — вялый

Как правило, установка значения null делает работу (для переменных типов, таких как int, вам нужно будет сделать ее версией с нулевым значением int?).

Если вы хотите использовать переменную только в течение короткого периода времени в более крупной функции, вы можете ограничить ее, например так:

Переменная будет длиться только до закрывающей скобки.

Если они не охватывают ваши обстоятельства, то можете ли вы уточнить, где вам нужно это сделать.

ответ дан 22 дек ’14, 22:12

Может быть, вы хотели бы освободить объект на которую ссылается переменная:

ответ дан 13 окт ’08, 12:10

NB это не гарантирует удаление объекта, к которому MyVar указывал. — Джо

Переменные типа значения не нужно сбрасывать. Они постоянно выделяются.

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

Если вы хотите использовать это для освобождения памяти, просто установите для всех ненужных объектов значение null и подождите, пока сборщик мусора выполнит свою работу.

Редактировать: Как отмечалось в комментариях, я не сказал, что сборщик мусора не начнет сборку сразу. Обычно это происходит, когда фреймворк пытается выделить память и не может найти достаточно свободной. Запустить сборку мусора «вручную» можно, но это не рекомендуется и может ухудшить поведение программы. Для большинства целей поведения сборщика мусора по умолчанию должно быть достаточно.

ответ дан 13 окт ’08, 13:10

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

@hangy: Это не так просто. Память объекта будет освобождена только при следующем запуске GC (для этого поколения), а не как только на него не ссылается переменная. Есть и другие сложности, такие как циклические ссылки (не проблема) и финализация. 300 символов мало! — Джон Скит

300 символов мало, да. 🙂 Я только что добавил это, потому что, просто используя ответ rslite, можно подумать, что объект исчез, как только для одной переменной установлено значение null. Вы правы, конечно, это не всегда так просто. — вялый

Сборщик мусора просматривает дерево ссылок от листа (в данном случае это объект, который, возможно, должен быть собран) к корню. Если он не укоренен, он будет собран. Другие, также некорневые объекты могут содержать ссылку, и она все равно будет собрана. — пользователь1228

@Will: Нет, он не переходит от листа к корню. Он использует отметку/разметку, обходя все известные корни и затем собирая все, что не отмечено. Вы не можете перемещаться по листу-> корню — объект не знает, что на него ссылается. — Джон Скит

Чтобы сбросить переменную среды, используйте тот же метод Environment.SetEnvironmentVariable, но передайте null или string.Empty в качестве значения. http://msdn.microsoft.com/en-us/library/96xafkes(v=vs.110).aspx

«Если значение пусто, а переменная среды, названная переменной, существует, переменная среды удаляется».

Name already in use

csharp_for_beginners / c20.md

  • Go to file T
  • Go to line L
  • Copy path
  • Copy permalink
  • Open with Desktop
  • View raw
  • Copy raw contents Copy raw contents

Copy raw contents

Copy raw contents

ГЛАВА 20. Небезопасный код, указатели, обнуляемые типы и разные ключевые слова

В этой главе рассматривается средство языка С#, ко­ торое обычно захватывает программистов врасплох. Это небезопасный код. В таком коде зачастую ис­ пользуются указатели. Совместно с небезопасным кодом указатели позволяют разрабатывать на C# приложения, которые обычно связываются с языком C++, высокой про­ изводительностью и системным кодом. Более того, благода­ ря включению небезопасного кода и указателей в состав C# в этом языке появились возможности, которые отсутствуют в Java.

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

В C# разрешается писать так называемый «небезопас­ ный» код. В этом странном на первый взгляд утвержде­ нии нет на самом деле ничего необычного. Небезопасным считается не плохо написанный код, а такой код, который не может быть выполнен под полным управлением в об­ щеязыковой исполняющей среде (CLR). Как пояснялось в главе 1, результатом программирования на C# обычно яв­ ляется управляемый код. Тем не менее этот язык програм­ мирования допускает написание кода, который не выпол­ няется под полным управлением в среде CLR. Такой неу­ правляемый код не подчиняется тем же самым средствам управления и ограничениям, что и управляемый код, и называется он небезопасным потому, что нельзя никак проверить, не выполняет ли он какое-нибудь опасное дей­ ствие. Следовательно, термин небезопасный совсем не означает, что коду присущи какие-то изъяны. Это просто означает, что код может выполнять действия, которые не подлежат контролю в управляемой среде.

Если небезопасный код может вызвать осложнения, то зачем вообще создавать та­ кой код? Дело в том, что управляемый код не позволяет использовать указатели. Если у вас имеется некоторый опыт программирования на С или C++, то вам должно быть известно, что указатели представляют собой переменные, предназначенные для хра­ нения адресов других объектов, т.е. они в какой-то степени похожи на ссылки в С#. Главное отличие указателя заключается в том, что он может указывать на любую об­ ласть памяти, тогда как ссылка всегда указывает на объект своего типа. Но поскольку указатель способен указывать практически на любую область памяти, то существует большая вероятность его неправильного использования. Кроме того, используя ука­ затели, легко допустить программные ошибки. Именно поэтому указатели не под­ держиваются при создании управляемого кода в С#. А поскольку указатели все-таки полезны и необходимы для некоторых видов программирования (например, утилит системного уровня), в C# разрешается создавать и использовать их. Но при этом все операции с указателями должны быть помечены как небезопасные, потому что они выполняются вне управляемой среды.

В языке C# указатели объявляются и используются таким же образом, как и в C/C++. Если вы знаете, как пользоваться ими в C/C++, то вам нетрудно будет сделать это и в С#. Но не забывайте, что главное назначение C# — создание управляемого кода. А спо­ собность этого языка программирования поддерживать неуправляемый код следует использовать для решения лишь особого рода задач. Это, скорее, исключение, чем правило для программирования на С#. По существу, для компилирования неуправ­ ляемого кода следует использовать параметр компилятора /unsafe.

Указатели составляют основу небезопасного кода, поэтому мы начнем его рассмо­ трение именно с них.

Основы применения указателей

Указатель представляет собой переменную, хранящую адрес какого-нибудь другого объекта, например другой переменной. Так, если в переменной х хранится адрес пере­ менной у, то говорят, что переменная х указывает на переменную у. Когда указатель указывает на переменную, то значение этой переменной может быть получено или изменено по указателю. Такие операции с указателями называют непрямой адресацией.

Переменные-указатели должны быть объявлены как таковые. Ниже приведена об­ щая форма объявления переменной-указателя:

где тип обозначает соотносимый тип, который не должен быть ссылочным. Это озна­ чает, что в C# нельзя объявить указатель на объект определенного класса. Соотноси­ мый тип указателя иногда еще называют базовым. Обратите внимание на положение знака * в объявлении указателя. Он должен следовать после наименования типа. А имя_переменной обозначает конкретное имя указателя-переменной.

Обратимся к конкретному примеру. Для того чтобы сделать переменную ip указа­ телем на значение типа int, необходимо объявить ее следующим образом.

А указатель типа float объявляется так, как показано ниже.

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

Тип данных, на которые будет указывать сам указатель, зависит от его соотносимого типа. Поэтому в приведенных выше примерах переменная ip может служить для ука­ зания на значение типа int, а переменная fp — для указания на значение типа float. Следует, однако, иметь в виду, что указателю ничто не мешает указывать на что угодно. Именно поэтому указатели потенциально небезопасны.

Если у вас есть опыт программирования на С/С++, то вы должны ясно понимать главное отличие в объявлении указателей в C# и C/C++. При объявлении указателя в C/C++ знак * не разделяет список переменных в объявлении. Поэтому в следующей строке кода:

объявляется указатель р типа int и переменная q типа int. Это равнозначно двум следующим объявлениям.

А в C# знак * является разделительным, и поэтому в объявлении

создаются две переменные-указателя. Это равнозначно двум следующим объявлениям.

Это главное отличие следует иметь в виду при переносе кода C/C++ на С#.

Операторы * и & в указателях

В указателях применяются два оператора: * и &. Оператор & является унарным и возвращает адрес памяти своего операнда. (Напомним, что для унарного оператора требуется единственный операнд.) Например, в следующем фрагменте кода:

в переменной ip сохраняется адрес памяти переменной num. Это адрес расположения переменной num в оперативной памяти компьютера. Он не имеет никакого отноше­ ния к значению переменной num. Поэтому в переменной ip содержится не значение 10, являющееся исходным для переменной num, а конкретный адрес, по которому эта переменная хранится в оперативной памяти. Операцию & можно рассматривать как возврат адреса той переменной, перед которой она указывается. Таким образом, при­ веденное выше присваивание словами можно описать так: «Переменная ip получает адрес переменной num.

Второй оператор, *, является дополнением оператора &. Этот унарный оператор на­ ходит значение переменной, расположенной по адресу, на который указывает его опе­ ранд. Следовательно, этот оператор обращается к значению переменной, на которую указывает соответствующий указатель. Так, если переменная ip содержит адрес памяти переменной num, как показано в предыдущем примере, то в следующей строке кода:

в переменной val сохраняется значение 10 переменной num, на которую указывает переменная ip. Операцию * можно рассматривать как получение значения по адресу. Поэтому приведенный выше оператор присваивания описывается словами следую­ щим образом: «Переменная val получает значение по адресу, хранящемуся в пере­ менной ip.»

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

В данном примере значение 100 присваивается переменной, на которую указывает переменная ip, т.е. переменной num. Поэтому приведенный выше оператор присваи­ вания описывается словами следующим образом: «Разместить значение 100 по адресу, хранящемуся в переменной ip.»

Применение ключевого слова unsafe

Любой код, в котором используются указатели, должен быть помечен как небезо­ пасный с помощью специального ключевого слова unsafe. Подобным образом мож­ но пометить конкретные типы данных (например, классы и структуры), члены класса (в том числе методы и операторы) или отдельные кодовые блоки как небезопасные. В качестве примера ниже приведена программа, где указатели используются в методе Main(), помеченном как небезопасный.

Эта программа дает следующий результат.

Применение модификатора fixed

В работе с указателями нередко используется модификатор fixed, который препят­ ствует удалению управляемой переменной средствами «сборки мусора». Потребность в этом возникает, например, в том случае, если указатель обращается к полю в объекте определенного класса. А поскольку указателю ничего не известно о действиях системы «сборки мусора», то он будет указывать не на тот объект, если удалить нужный объект. Ниже приведена общая форма модификатора fixed:

где р обозначает указатель, которому присваивается адрес объекта. Этот объект будет оставаться на своем текущем месте в памяти до конца выполнения кодового блока. В качестве адресата оператора fixed может быть также указано единственное выраже­ ние, а не целый кодовый блок. Модификатор fixed допускается использовать только в коде, помеченном как небезопасный. Кроме того, несколько указателей с модифика­ тором fixed могут быть объявлены списком через запятую.

Ниже приведен пример применения модификатора fixed.

Вот к какому результату приводит выполнение этой программы.

В данном примере модификатор fixed препятствует удалению объекта о. А по­ скольку переменная р указывает на переменную экземпляра о.num, то она будет ука­ зывать на недостоверную область памяти, если удалить объект о.

Доступ к членам структуры с помощью указателя

Указатель может указывать на объект типа структуры при условии, что структура не содержит ссылочные типы данных. Для доступа к члену структуры с помощью ука­ зателя следует использовать оператор-стрелку (->), а не оператор-точку (.). Напри­ мер, доступ к членам структуры

осуществляется следующим образом.

Арифметические операции над указателями

Над указателями можно выполнять только четыре арифметические операции: ++, —, + и -. Для того чтобы стало понятнее, что именно происходит в арифметических операциях над указателями, рассмотрим сначала простой пример. Допустим, что пе­ ременная p1 является указателем с текущим значением 2000, т.е. она содержит адрес 2000. После выполнения выражения

переменная p1 будет содержать значение 2004, а не 2001! Дело в том, что после каждого инкрементирования переменная p1 указывает на следующее значение типа int. А по­ скольку тип int представлен в C# 4 байтами, то в результате инкрементирования зна­ чение переменной p1 увеличивается на 4. Справедливо и обратное: при каждом декре­ ментировании переменной p1 ее значение уменьшается на 4. Например выражение

приводит к тому, что значение переменной p1 становится равным 1996, если раньше оно было равно 2000!

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

переменная p1 будет указывать на девятый элемент ее соотносимого типа по отноше­ нию к элементу, на который она указывает в настоящий момент.

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

Кроме сложения и вычитания целого числа из указателя, а также вычитания двух указателей, другие арифметические операции над указателями не разрешаются. В част­ ности, к указателям нельзя добавлять или вычитать из них значения типа float или double. Не допускаются также арифметические операции над указателями типа void*.

Для того чтобы проверить на практике результаты арифметических операций над указателями, выполните приведенную ниже короткую программу, где выводятся фи­ зические адреса, на которые указывает целочисленный указатель (ip) и указатель с пла­ вающей точкой одинарной точности (fp). Понаблюдайте за изменениями каждого из этих указателей по отношению к их соотносимым типам на каждом шаге цикла.

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

Как следует из приведенного выше результата, арифметические операции выпол­ няются над указателями относительно их соотносимого типа. Так, значения типа int занимают в памяти 4 байта, а значения типа double — 8 байтов, и поэтому их адреса изменяются с приращением именно на эти величины.

Указатели можно сравнивать с помощью таких операторов отношения, как ==, < и >. Но для того чтобы результат сравнения указателей оказался содержательным, оба указателя должны быть каким-то образом связаны друг с другом. Так, если перемен­ ные p1 и р2 являются указателями на две разные и не связанные вместе переменные, то любое их сравнение, как правило, не имеет никакого смысла. Но если переменные p1 и р2 указывают на связанные вместе переменные, например на элементы одного массива, то их сравнение может иметь определенный смысл.

Указатели и массивы

В C# указатели и массивы связаны друг с другом. Например, при указании имени массива без индекса в операторе с модификатором fixed формируется указатель на начало массива. В качестве примера рассмотрим следующую программу.

Ниже приведен результат выполнения этой программы.

Как следует из приведенного выше результата, выражения

оказываются одинаковыми. Но поскольку вторая форма более лаконична, то она чаще используется в программировании, когда требуется указатель на начало массива.

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

Ниже приведен результат выполнения этой программы.

Как следует из результата выполнения приведенной выше программы, общая фор­ ма выражения с указателем

может быть заменена следующим синтаксисом индексирования массива.

Что касается индексирования указателей, то необходимо иметь в виду следующее. Во-первых, при таком индексировании контроль границ массива не осуществляется. Поэтому указатель может обращаться к элементу вне границ массива. И во-вторых, для указателя не предусмотрено свойство Length, определяющее длину массива. Поэ­ тому, если используется указатель, длина массива заранее неизвестна.

Указатели и строки

Символьные строки реализованы в C# в виде объектов. Тем не менее отдельные сим­ волы в строке могут быть доступны по указателю. Для этого указателю типа char* присваивается адрес начала символьной строки в следующем операторе с модифика­ тором fixed.

После выполнения оператора с модификатором fixed переменная р будет указы­ вать на начало массива символов, составляющих строку. Этот массив оканчивается символом конца строки, т.е. нулевым символом. Поэтому данное обстоятельство мож­ но использовать для проверки конца массива. В С/С++ строки реализуются в виде мас­ сивов, оканчивающихся символом конца строки, а следовательно, получив указатель типа char* на строку, ею можно манипулировать таким же образом, как и в C/C++.

Ниже приведена программа, демонстрирующая доступ к символьной строке по указателю типа char*.

Эта программа дает следующий результат.

Многоуровневая непрямая адресация

Один указатель может указывать на другой, а тот, свою очередь, — на целевое зна­ чение. Это так называемая многоуровневая непрямая адресация, или применение указа­ телей на указатели. Такое применение указателей может показаться, на первый взгляд, запутанным. Для прояснения принципа многоуровневой непрямой адресации обра­ тимся за помощью к рис. 20.1. Как видите, значением обычного указателя является адрес переменной, содержащей требуемое значение. Если же применяется указатель на указатель, то первый из них содержит адрес второго, указывающего на переменную, содержащую требуемое значение.

Рис. 20.1. Одно- и многоуровневая непрямая адресация

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

Переменная, являющаяся указателем на указатель, должна быть объявлена как та­ ковая. Для этого достаточно указать дополнительный знак * после имени типа пере­ менной. Например, в следующем объявлении компилятор уведомляется о том, что переменная q является указателем на указатель и относится к типу int.

Следует, однако, иметь в виду, что переменная q является указателем не на целое значение, а на указатель типа int.

Для доступа к целевому значению, косвенно адресуемому по указателю на указа­ тель, следует дважды применить оператор *, как в приведенном ниже примере.

Результатом выполнения этой программы будет выведенное на экран значение 10 переменной х. В данной программе переменная р объявляется как указатель на значе­ ние типа int, а переменная q — как указатель на указатель типа int.

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

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

Для того чтобы присвоить адрес переменной var типа int третьему элементу мас­ сива указателей, достаточно написать следующую строку кода.

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

Во время работы с небезопасным кодом иногда полезно знать размер в байтах одного из встроенных в C# типов значений. Для получения этой информации служит оператор sizeof. Ниже приведена его общая форма:

где тип обозначает тот тип, размер которого требуется получить. Вообще говоря, опе­ ратор sizeof предназначен главным образом для особых случаев и, в частности, для работы со смешанным кодом: управляемым и неуправляемым.

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

где р обозначает указатель, получающий адрес области памяти, достаточной для хра­ нения объектов, имеющих указанный тип, в количестве, которое обозначает размер. Если же в стеке недостаточно места для распределения памяти, то генерируется ис­ ключение System.StackOverflowException. И наконец, оператор stackalloc мож­ но использовать только в небезопасном коде.

Как правило, память для объектов выделяется из кучи — динамически распределя­ емой свободной области памяти. А выделение памяти из стека является исключением. Ведь переменные, располагаемые в стеке, не удаляются средствами «сборки мусора», а существуют только в течение времени выполнения метода, в котором они объявляют­ ся. После возврата из метода выделенная память освобождается. Преимущество при­ менения оператора stackalloc заключается, в частности, в том, что в этом случае не нужно беспокоиться об очистке памяти средствами «сборки мусора».

Ниже приведен пример применения оператора stackalloc.

Вот к какому результату приводит выполнение кода из данного примера.

Создание буферов фиксированного размера

Ключевое слово fixed находит еще одно применение при создании одномерных массивов фиксированного размера. В документации на C# такие массивы называются буферами фиксированного размера. Такие буферы всегда являются членами структуры. Они предназначены для создания структуры, в которой содержатся элементы массива, образующие буфер. Когда элемент массива включается в состав структуры, в ней, как правило, хранится лишь ссылка на этот массив. Используя буфер фиксированного раз­ мера, в структуре можно разместить весь массив. В итоге получается структура, при­ годная в тех случаях, когда важен ее размер, как, например, в многоязыковом програм­ мировании, при согласовании данных, созданных вне программы на С#, или же когда требуется неуправляемая структура, содержащая массив. Но буферы фиксированного размера можно использовать только в небезопасном коде.

Для создания буфера фиксированного размера служит следующая общая форма:

где тип обозначает тип данных массива; имя_буфера — конкретное имя буфера фик­ сированного размера; размер — количество элементов, образующих буфер. Буферы фиксированного размера могут указываться только в структуре.

Для того чтобы стала очевиднее польза от буферов фиксированного размера, рас­ смотрим ситуацию, в которой программе ведения счетов, написанной на C++, тре­ буется передать информацию о банковском счете. Допустим также, что учетная запись каждого счета организована так, как показано ниже.

Name Строка длиной 80 байтов, состоящая из 8-разрядных символов в коде ASCII
Balance Числовое значение типа double длиной 8 байтов
ID Числовое значение типа long длиной 8 байтов

В программе на C++ каждая структура содержит массив Name, тогда как в програм­ ме на C# в такой структуре хранится лишь ссылка на массив. Поэтому для правильного представления данных из этой структуры в C# требуется буфер фиксированного раз­ мера, как показано ниже.

Когда буфер фиксированного размера используется вместо массива Name, каждый экземпляр структуры FixedBankRecord будет содержать все 80 байтов массива Name. Именно таким образом структура и организована в программе на C++. Следовательно, общий размер структуры FixedBankRecord окажется равным 96, т.е. сумме ее членов. Ниже приведена программа, демонстрирующая этот факт.

Эта программа дает следующий результат.

Размер структуры FixedBankRecord оказывается в точности равным сумме ее чле­ нов, но так бывает далеко не всегда со структурами, содержащими буферы фиксиро­ ванного размера. Ради повышения эффективности кода общая длина структуры мо­ жет быть увеличена для выравнивания по четной границе, например по границе слова. Поэтому общая длина структуры может оказаться на несколько байтов больше, чем сумма ее членов, даже если в ней содержатся буферы фиксированного размера. Как правило, аналогичное выравнивание длины структуры происходит и в C++. Следует, однако, иметь в виду возможные отличия в этом отношении.

И наконец, обратите внимание на то, как в данной программе создается буфер фик­ сированного размера вместо массива Name.

Как видите, размер массива указывается после его имени. Такое обозначение обычно принято в C++ и отличается в объявлениях массивов в С#. В данном операторе распре­ деляется по 80 байтов памяти в пределах каждого объекта типа FixedBankRecord.

Начиная с версии 2.0, в C# внедрено средство, обеспечивающее изящное решение типичной и не очень приятной задачи распознавания и обработки полей, не содержа­ щих значения, т.е. неинициализированных полей. Это средство называется обнуляемым типом. Для того чтобы стала более понятной суть данной задачи, рассмотрим пример простой базы данных заказчиков, в которой хранится запись с именем, адресом, иден­ тификационным номером заказчика, номером счета-фактуры и текущим остатком на счету. В подобной ситуации может быть вполне создан элемент данных заказчика, в ко­ тором одно или несколько полей не инициализированы. Например, заказчик может просто запросить каталог продукции, и в этом случае номер счета-фактуры не потре­ буется, а значит, его поле окажется неиспользованным.

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

Основы применения обнуляемых типов

Обнуляемый тип — это особый вариант типа значения, представленный структу­ рой. Помимо значений, определяемых базовым типом, обнуляемый тип позволяет хранить пустые значения (null). Следовательно, обнуляемый тип имеет такой же диа­ пазон представления чисел и характеристики, как и его базовый тип. Он предоставляет дополнительную возможность обозначить значение, указывающее на то, что перемен­ ная данного типа не инициализирована. Обнуляемые типы являются объектами типа System.Nullаble<Т> , где Т — тип значения, которое не должно быть обнуляемым.

ПРИМЕЧАНИЕ Обнуляемые эквиваленты могут быть только у типов значений.

Обнуляемый тип может быть указан двумя способами. Во-первых, объекты типа Nullable<T> , определенного в пространстве имен System, могут быть объявлены яв­ ным образом. Так, в приведенном ниже примере создаются обнуляемые переменные типа int и bool.

И во-вторых, обнуляемый тип объявляется более кратким и поэтому чаще исполь­ зуемым способом с указанием знака ? после имени базового типа. В приведенном ниже примере демонстрируется более распространенный способ объявления обну­ ляемых переменных типа int и bool.

Когда в коде применяются обнуляемые типы, создаваемый обнуляемый объект обычно выглядит следующим образом.

В данной строке кода переменная count явно инициализируется пустым значени­ ем (null). Это вполне соответствует принятому правилу: прежде чем использовать переменную, ей нужно присвоить значение. В данном случае присваиваемое значение означает, что переменная не определена.

Значение может быть присвоено обнуляемой переменной обычным образом, по­ скольку преобразование базового типа в обнуляемый определено заранее. Например, в следующей строке кода переменной count присваивается значение 100.

Определить, имеет переменная обнуляемого типа пустое или конкретное значение, можно двумя способами. Во-первых, можно проверить переменную на пустое значе­ ние. Так, если переменная count объявлена так, как показано выше, то в следующей строке определяется, имеет ли эта переменная конкретное значение.

Если переменная count не является пустой, то она содержит конкретное значение. И во-вторых, можно воспользоваться доступным только для чтения свойством HasValue типа Nullable<T> , чтобы определить, содержит ли переменная обнуляе­ мого типа конкретное значение. Это свойство показано ниже.

Свойство HasValue возвращает логическое значение true, если экземпляр объекта, для которого оно вызывается, содержит конкретное значение, а иначе оно возвраща­ ет логическое значение false. Ниже приведен пример, в котором конкретное значе­ ние обнуляемого объекта count определяется вторым способом с помощью свойства HasValue.

Если обнуляемый объект содержит конкретное значение, то получить это значение можно с помощью доступного только для чтения свойства Value типа Nullable.

Свойство Value возвращает экземпляр обнуляемого объекта, для которо­ го оно вызывается. Если же попытаться получить с помощью этого свойства зна­ чение пустой переменной, то в итоге будет сгенерировано исключение System. InvalidOperationException. Кроме того, значение экземпляра обнуляемого объек­ та можно получить путем приведения к его базовому типу.

В следующей программе демонстрируется основной механизм обращения с обну­ ляемым типом.

Вот к какому результату приводит выполнение этой программы.

Применение обнуляемых объектов в выражениях

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

В приведенной ниже программе демонстрируется применение обнуляемых типов в выражениях.

При выполнении этой программы получается следующий результат.

Попытка преобразовать обнуляемый объект в его базовый тип путем при­ ведения типов обычно приводит к генерированию исключения System. InvalidOperationException, если обнуляемый объект содержит пустое значение. Это может произойти, например, в том случае, если значение обнуляемого объекта присваивается переменной его базового типа с помощью приведения типов. Появле­ ния данного исключения можно избежать, если воспользоваться оператором . на­ зываемым нулеобъединяющим оператором. Этот оператор позволяет указать значение, которое будет использоваться по умолчанию, если обнуляемый объект содержит пу­ стое значение. Он также исключает потребность в приведении типов.

Ниже приведена общая форма оператора .

Если обнуляемый_объект содержит конкретное значение, то результатом опера­ ции ?? будет именно это значение. В противном случае результатом операции ?? ока­ жется значение_по_умолчанию.

Например, в приведенном ниже фрагменте кода переменная balance содержит пустое значение. Вследствие этого переменной currentBalance присваивается зна­ чение 0.0, используемое по умолчанию, и тем самым устраняется причина для гене­ рирования исключения.

В следующем фрагменте кода переменной balance присваивается значение 123.75.

Теперь переменная currentBalance содержит значение 123.75 переменной balance.

И еще одно замечание: выражение в правой части оператора ?? вычисляется толь­ ко в том случае, если выражение в левой его части не содержит значение. Этот факт демонстрируется в приведенной ниже программе.

В этой программе метод GetZeroBal() не вызывается, поскольку переменная balance содержит конкретное значение. Как пояснялось выше, если выражение в ле­ вой части оператора ?? содержит конкретное значение, то выражение в правой его части не вычисляется.

Обнуляемые объекты, операторы отношения и логические операторы

Обнуляемые объекты могут использоваться в выражениях отношения таким же образом, как и соответствующие объекты необнуляемого типа. Но они должны под­ чиняться следующему дополнительному правилу: когда два обнуляемых объекта срав­ ниваются в операциях сравнения <, >, <= или >=, то их результат будет ложным, если любой из обнуляемых объектов оказывается пустым, т.е. содержит значение null. В качестве примера рассмотрим следующий фрагмент кода.

// Здесь переменная lower определена, а переменная upper не определена.

В данном случае проверка того, что значение одной переменой меньше значения другой, дает ложный результат. Хотя это и не совсем очевидно, как, впрочем, и следую­ щая проверка противоположного характера.

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

Тем не менее с помощью операторов == и != можно проверить, содержит ли обну­ ляемый объект пустое значение. Например, следующая проверка вполне допустима и дает истинный результат.

Если в логическом выражении участвуют два объекта типа bool?, то его результат может иметь одно из трех следующих значений: true (истинное), false (ложное) или null (неопределенное). Ниже приведены результаты применения логических опера­ торов & и | к объектам типа bool?.

P Q P | Q P & Q
true null true null
false null null false
null true true null
null false null false
null null null null

И наконец, если логический оператор ! применяется к значению типа bool?, ко­ торое является пустым (null), то результат этой операции будет неопределенным (null).

Начиная с версии 2.0, в C# появилась возможность разделять определение класса, структуры или интерфейса на две или более части с сохранением каждой из них в от­ дельном файле. Это делается с помощью контекстного ключевого слова partial. Все эти части объединяются вместе во время компиляции программы.

Если модификатор partial используется для создания частичного типа, то он принимает следующую общую форму:

где имя_типа обозначает имя класса, структуры или интерфейса, разделяемого на ча­ сти. Каждая часть получающегося частичного типа должна указываться вместе с моди­ фикатором partial.

Рассмотрим пример разделения простого класса, содержащего координаты XY, на три отдельных файла. Ниже приведено содержимое первого файла.

Далее следует содержимое второго файла.

И наконец, содержимое третьего файла. partial class XY < public int Y < get; set; >>

Читать:
Как из visual studio отправлять проект на github

Похожие статьи