Тверской государственный университет
Опубликован: 22.11.2005 | Доступ: свободный | Студентов: 30222 / 1737 | Оценка: 4.31 / 3.69 | Длительность: 28:26:00
ISBN: 978-5-9556-0050-5
Лекция 7:

Присваивание и встроенные функции

< Лекция 6 || Лекция 7: 123 || Лекция 8 >
Аннотация: Присваивание. Новинка C# - определенное присваивание. Классы Math, Random и встроенные функции.

Присваивание

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

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

/// <summary>
/// анализ присваивания
/// </summary>
public void Assign()
{
	double x,y,z,w =1, u =7, v= 5;
	x = y = z = w =(u+v+w)/(u-v-w);
}//Assign

По мере изложения в метод Assign будут добавляться фрагменты кода, связанные с рассматриваемой темой присваивания.

О семантике присваивания говорилось уже достаточно много. Но следует внести еще некоторые уточнения. Правильно построенное выражение присваивания состоит из левой и правой части. Левая часть - это список переменных, в котором знак равенства выступает в качестве разделителя. Правая часть - это выражение. Выражение правой части вычисляется, при необходимости приводится к типу переменных левой части, после чего все переменные левой части получают значение вычисленного выражения. Последние действия можно рассматривать как побочный эффект операции присваивания. Заметьте, все переменные в списке левой части должны иметь один тип или неявно приводиться к одному типу. Операция присваивания выполняется справа налево, поэтому вначале значение выражения получит самая правая переменная списка левой части, при этом значение самого выражения не меняется. Затем значение получает следующая справа по списку переменная - и так до тех пор, пока не будет достигнут конец списка. Так что реально можно говорить об одновременном присваивании, в котором все переменные списка получают одно и то же значение. В нашем примере, несмотря на то, что переменная w первой получит значение, а выражение в правой части зависит от w, все переменные будут иметь значение 13.0. Рассмотрим еще один фрагмент кода:

bool b;
x=5; y=6;
//b= x=y;
//if (x=y) z=1;else z=-1;

В программе на языке C++ можно было снять комментарии с операторов, и этот фрагмент кода компилировался и выполнялся бы без ошибок. Другое дело, что результат мог быть некорректен, поскольку, вероятнее всего, операция присваивания " x=y " написана по ошибке и ее следует заменить операцией эквивалентности " x==y ". В языке C# оба закомментированных оператора, к счастью, приведут к ошибке трансляции, поскольку результат присваивания имеет тип double, для которого нет неявного преобразования в тип bool. На C# такая программа будет выполняться, только если x и y будут иметь тип bool, но в этом случае, возможно, применение операции присваивания имеет смысл. С типами double корректная программа на C# может быть такой:

x =y;
b= (y!=0);
if(y!=0) z=1; else z = -1;

В программе появился лишний оператор, но исчезла двусмысленность, порождаемая операцией присваивания.

Специальные случаи присваивания

В языке C++ для двух частных случаев присваивания предложен отдельный синтаксис. Язык C# наследовал эти полезные свойства. Для присваиваний вида " x=x+1 ", в которых переменная увеличивается или уменьшается на единицу, используются специальные префиксные и постфиксные операции " ++ " и " -- ". Другой важный частный случай - это присваивания вида:

X = X <operator> (expression)

Для таких присваиваний используется краткая форма записи:

X <operator>= expression

В качестве операции разрешается использовать арифметические, логические (побитовые) операции и операции сдвига языка C#. Семантика такого присваивания достаточно очевидна, и я ограничусь простым примером:

x += u+v; y /=(u-v);
b &= (x<y);

Однако и здесь есть один подводный камень, когда x= x+a не эквивалентно x +=a. Рассмотрим следующий пример:

byte b3 = 21;
b3 +=1; //Это допустимо
//b3 = b3+1; //А это недопустимо:результат типа int

Закомментированный оператор приведет к ошибке компиляции, поскольку правая часть имеет тип int, а неявное преобразование к типу byte отсутствует. Следует понимать, что преимущество первой формы записи - только кажущееся: если при инициализации переменная b получит допустимое значение 255, то следующий оператор присваивания в краткой форме не выдаст ошибки, но даст неверный результат, а это - самое худшее, что может случиться в программе. Так что надежнее пользоваться полной формой записи присваивания, не экономя на паре символов.

Определенное присваивание

Присваивание в языке C# называется определенным присваиванием (definite assignment). В этом термине отражен тот уже обсуждавшийся факт, что все используемые в выражениях переменные должны быть ранее инициализированы и иметь определенные значения. Единственное, за чем компилятор не следит, так это за инициализацией переменных массива. Для них используется инициализация элементов, задаваемая по умолчанию. Приведу пример:

//определенное присваивание
int an =0 ; //переменные должны быть инициализированы
for (int i= 0;i<5;i++)
{an =i+1;}
x+=an; z+=an; y = an;
string[] ars = new string[3];
double[] ard = new double[3];
for (int i= 0;i<3;i++)
{
	//массивы могут быть без инициализации
	ard[i] += i+1;
	ars[i] += i.ToString()+1;
	Console.WriteLine("ard[" +i + "]=" +ard[i] +
		";  ars[" +i + "]=" +ars[i]);
}

Заметьте, в этом фрагменте переменная an обязана быть инициализированной, а массивы ard и ars не инициализируются и спокойно участвуют в вычислениях.

Еще раз о семантике присваивания

Подводя итоги рассмотрения присваивания x=e, следует отметить, что семантика присваивания далеко не столь проста, как может показаться с первого взгляда. Напомню, что деление типов на значимые и ссылочные приводит к двум семантикам присваивания. Будет ли семантика значимой или ссылочной - определяется типом левой части присваивания. Переменные значимых типов являются единоличными владельцами памяти, в которой хранятся их значения. При значимом присваивании память для хранения значений остается той же - меняются лишь сами значения, хранимые в ней. Переменные ссылочных типов (объекты) являются ссылками на реальные объекты динамической памяти. Ссылки могут разделять одну и ту же область памяти - ссылаться на один и тот же объект. Ссылочное присваивание - это операция над ссылками. В результате ссылочного присваивания ссылка начинает указывать на другой объект.

Рассмотрим объявления:

int x=3, y=5;
object obj1, obj2;

Здесь объявлены четыре сущности: две переменные значимого типа и две - объектного. Значимые переменные x и y проинициализированы и имеют значения, объектные переменные obj1 и obj2 являются пустыми ссылками со значением void. Рассмотрим присваивания:

obj1 = x; obj2 = y;

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

< Лекция 6 || Лекция 7: 123 || Лекция 8 >
Александр Галабудник
Александр Галабудник

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

Александра Гусева
Александра Гусева