Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 57 58 59 60 61 62 63 64 65 ... 407
добавьте к операторам верхнего уровня следующую локальную функцию:

// Присваивание двух внутренних типов значений дает

// в результате две независимые переменные в стеке.

static void ValueTypeAssignment()

{

  Console.WriteLine("Assigning value typesn");

  Point p1 = new Point(10, 10);

  Point p2 = p1;

  // Вывести значения обеих переменных Point.

  p1.Display();

  p2.Display();

  // Изменить pl.X и снова вывести значения переменных.

  // Значение р2.Х не изменилось.

  p1.X = 100;

  Console.WriteLine("n=> Changed p1.Xn");

  p1.Display();

  p2.Display();

}

Здесь создается переменная типа Point(p1), которая присваивается другой переменной типа Point(р2). Поскольку Point — тип значения, в стеке находятся две копии Point, каждой из которых можно манипулировать независимым образом. Поэтому при изменении значения p1.X значение р2.X остается незатронутым:

Assigning value types

X = 10, Y = 10

X = 10, Y = 10

=> Changed p1.X

X = 100, Y = 10

X = 10, Y = 10

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

// Классы всегда являются ссылочными типами.

class PointRef

{

  // Те же самые члены, что и в структуре Point...

  // Не забудьте изменить имя конструктора на PointRef!

  public PointRef(int xPos, int yPos)

  {

    X = xPos;

    Y = yPos;

  }

}

Задействуйте готовый тип PointRef в следующем новом методе. Обратите внимание, что помимо использования класса PointRef вместо структуры Point код идентичен коду метода ValueTypeAssignment():

static void ReferenceTypeAssignment()

{

  Console.WriteLine("Assigning reference typesn");

  PointRef p1 = new PointRef(10, 10);

  PointRef p2 = p1;

  // Вывести значения обеих переменных PointRef.

  p1.Display();

  p2.Display();

  // Изменить pl.X и снова вывести значения.

  p1.X = 100;

  Console.WriteLine("n=> Changed p1.Xn");

  p1.Display();

  p2.Display();

}

В рассматриваемом случае есть две ссылки, указывающие на тот же самый объект в управляемой куче. Таким образом, когда значение X изменяется с использованием ссылки p1, изменится также и значение р2.X. Вот вывод, получаемый в результате вызова этого нового метода:

Assigning reference types

X = 10, Y = 10

X = 10, Y = 10

=> Changed p1.X

X = 100, Y = 10

X = 100, Y = 10

Использование типов значений, содержащих ссылочные типы

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

class ShapeInfo

{

  public string InfoString;

  public ShapeInfo(string info)

  {

    InfoString = info;

  }

}

Далее представим, что переменная типа ShapeInfo должна содержаться внутри типа значения по имени Rectangle. Кроме того, в типе Rectangle предусмотрен специальный конструктор, который позволяет вызывающему коду указывать значение для внутренней переменной-члена типа ShapeInfo. Вот полное определение типа Rectangle:

struct Rectangle

{

  // Структура Rectangle содержит член ссылочного типа.

  public ShapeInfo RectInfo;

  public int RectTop, RectLeft, RectBottom, RectRight;

  public Rectangle(string info, int top, int left, int bottom, int right)

  {

    RectInfo = new ShapeInfo(info);

    RectTop = top; RectBottom = bottom;

    RectLeft = left; RectRight = right;

  }

  public void Display()

  {

    Console.WriteLine("String = {0}, Top = {1}, Bottom = {2}, " +

      "Left = {3}, Right = {4}",

      RectInfo.InfoString, RectTop, RectBottom, RectLeft, RectRight);

  }

}

Здесь ссылочный тип содержится внутри типа значения. Возникает важный вопрос: что произойдет в результате присваивания одной переменной типа Rectangle другой переменной того же типа? Учитывая то, что уже известно о типах значений, можно корректно предположить, что целочисленные данные (которые на самом деле являются структурой — System.Int32)должны быть независимой сущностью для каждой переменной Rectangle. Но что можно сказать о внутреннем ссылочном типе? Будет ли полностью скопировано состояние этого объекта или же только ссылка на него? Чтобы получить ответ, определите следующий метод и вызовите его:

static void ValueTypeContainingRefType()

{

  // Создать первую переменную Rectangle.

  Console.WriteLine("-> Creating r1");

  Rectangle r1 = new Rectangle("First Rect", 10, 10, 50, 50);

  // Присвоить новой переменной Rectangle переменную r1.

  Console.WriteLine("-> Assigning r2 to r1");

  Rectangle r2 = r1;

  // Изменить некоторые значения в r2.

  Console.WriteLine("-> Changing values of r2");

  r2.RectInfo.InfoString = "This is new info!";

  r2.RectBottom = 4444;

  // Вывести значения из обеих переменных Rectangle.

  r1.Display();

  r2.Display();

}

Вывод будет таким:

-> Creating r1

-> Assigning r2 to r1

-> Changing values of r2

String = This is new info!, Top = 10, Bottom = 50, Left = 10, Right = 50

String = This is new info!, Top = 10, Bottom = 4444, Left = 10, Right = 50

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

1 ... 57 58 59 60 61 62 63 64 65 ... 407
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен бесплатно.

Оставить комментарий