Опубликован: 25.03.2010 | Доступ: свободный | Студентов: 1447 / 157 | Оценка: 4.31 / 4.00 | Длительность: 25:42:00
Лекция 20:

Консольные приложения в С#

< Лекция 19 || Лекция 20: 1234567891011

Типы данных в C#

В C# различают структурные типы ( reference-based ) и ссылочные ( value-based ). Переменная структурного типа представляет закрепленное за ней само значение оперативной памяти. Переменная ссылочного типа содержит лишь адрес области оперативной памяти с размещаемым в этой области значением. Все структурные переменные размещаются на стеке, а ссылочные - на управляемой куче ( managed heap ). К структурным типам относятся перечисления и структуры, а к ссылочным - классы, массивы, указатели, интерфейсы.

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

  • Создайте консольное приложение C# или модифицируйте уже созданное. Заполните файл с точкой входа следующим кодом
using System;
  
namespace Test
{
struct FOO
{
public int x, y;
}
  
class ValRefClass
{
static void Main(string[] args)
  {
  // Создаем объект на стеке
  // При создании структуры с конструктором по умолчанию
  // ключевое слово new является необязательным, 
  // а можно просто FOO f1;
  FOO f1 = new FOO();
  f1.x = 100;
  f1.y = 100;
  
  // Создается объект на стеке без ключевого слова new
  // и осуществляется побитовое копирование в другую память
  FOO f2 = f1;
  
  // Вывод до изменения
  Console.WriteLine("Содержимое объектов 
    f1 и f2 до изменения");
  Console.WriteLine("Объект f1: x={0}; 
    y={1}", f1.x, f1.y);
  Console.WriteLine("Объект f2: x={0}; 
    y={1}", f2.x, f2.y);
  
  // Внесение изменений
  f1.x = 1; f1.y = 2; f2.x = 3; f2.y = 4;
  // Вывод после изменения
  Console.WriteLine("\nСодержимое объектов 
    f1 и f2 после изменения");
  Console.WriteLine("Объект f1: x={0}; 
    y={1}", f1.x, f1.y);
  Console.WriteLine("Объект f2: x={0}; 
    y={1}", f2.x, f2.y);
  
  while(true);
  }
  }
}
Листинг 20.10 . Код для переменных структурного типа

Результаты экрана

Содержимое объектов f1 и f2 до изменения 
Объект f1: x=100; y=100
Объект f2: x=100; y=100
  
Содержимое объектов f1 и f2 после изменения 
Объект f1: x=1; y=2
Объект f2: x=3; y=4
  • Измените в исходном коде для типа FOO ключевое слово struct на class, тем самым сразу получим ссылочный тип. Запустите программу - получится результат
using System;
  
namespace Test
{
class FOO
{
public int x, y;
}
  
class ValRefClass
{
  static void Main(string[] args)
  {
  // Создаем объект на куче
  // Теперь слово new является обязательным,  а также
  // обазательны круглые скобки конструктора по умолчанию
  FOO f1 = new FOO();
  f1.x = 100;
  f1.y = 100;
  
  // Создается не объект, а ссылку, и после копирования
  // адреса получаем псевдоним на ту же область памяти
  FOO f2 = f1;
  
  // Вывод до изменения
  Console.WriteLine("Содержимое объектов f1 и 
    f2 до изменения");
  Console.WriteLine("Объект f1: x={0}; 
    y={1}", f1.x, f1.y);
  Console.WriteLine("Объект f2: x={0}; 
    y={1}", f2.x, f2.y);
  
  // Внесение изменений
  f1.x = 1; f1.y = 2; f2.x = 3; f2.y = 4;
  // Вывод после изменения
  Console.WriteLine("\nСодержимое объектов f1 и 
    f2 после изменения");
  Console.WriteLine("Объект f1: x={0}; 
    y={1}", f1.x, f1.y);
  Console.WriteLine("Объект f2: x={0}; 
    y={1}", f2.x, f2.y);
  
  while(true);
  }
  }
}
Листинг 20.11 . Код для переменных ссылочного типа

Результаты экрана

Содержимое объектов f1 и f2 до изменения 
Объект f1: x=100; y=100
Объект f2: x=100; y=100
  
Содержимое объектов f1 и f2 после изменения 
Объект f1: x=3; y=4
Объект f2: x=3; y=4

Здесь мы видим, что в обоих случаях мы изменяли один и тот же объект, который и сохранил последние значения. Теперь f1 и f2 - не более чем ссылки на один и тот же диапазон памяти в области управляемой кучи.

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

Следующий код и полученный результат иллюстрируют сказанное

using System;
  
namespace Test
{
class FOO
{
public int x, y;
}
  
class ValRefClass
{
static void Main(string[] args)
  {
  // Создаем первый объект на куче
  FOO f1 = new FOO();
  f1.x = 100;
  f1.y = 100;
  
  // Создается второй объект на куче
  FOO f2 = new FOO();
  f2.x = 100;
  f2.y = 100;
  
  // Вывод до изменения
  Console.WriteLine("Содержимое объектов f1 и 
    f2 до изменения");
  Console.WriteLine("Объект f1: x={0}; 
    y={1}", f1.x, f1.y);
  Console.WriteLine("Объект f2: x={0}; 
    y={1}", f2.x, f2.y);
  
  // Внесение изменений
  f1.x = 1; f1.y = 2; f2.x = 3; f2.y = 4;
  // Вывод после изменения
  Console.WriteLine("\nСодержимое объектов 
    f1 и f2 после изменения");
  Console.WriteLine("Объект f1: x={0}; 
    y={1}", f1.x, f1.y);
  Console.WriteLine("Объект f2: x={0}; 
    y={1}", f2.x, f2.y);
  
  while(true);
  }
  }
}
Листинг 20.12 . Код для переменных ссылочного типа

Результаты экрана

Содержимое объектов f1 и f2 до изменения 
Объект f1: x=100; y=100
Объект f2: x=100; y=100
  
Содержимое объектов f1 и f2 после изменения 
Объект f1: x=1; y=2
Объект f2: x=3; y=4

Общие сравнения структурных и ссылочных типов

Поставим некоторые предварительные вопросы и ответы на них в таблице

Таблица 20.3 . Сравнение структурных и ссылочных типов
Вопрос Структурные типы Ссылочные типы
Место размещения Стек Управляемая куча
Как будет представлена переменная, которой в качестве значения присвоен этот тип В виде локальной побитовой копии типа В виде указателя на область оперативной памяти, относящейся к объекту этого типа
Что может являться для них базовым типом Эти типы могут производиться только напрямую от типа System.ValueType Могут производиться от любого другого типа, кроме System.ValueType, если этот тип не является закрытым
Может ли этот тип выступать производителем другого типа Нет. Структурные типы всегда закрыты и дополнение их другими свойствами не предусмотрено. Поэтому они не могут выступать при наследовании в качестве базового для других типов Да, если этот ссылочный тип не определен внутренне как закрытый
Как производится передача параметров этого типа в функцию Только по значению, т.е. вызывающей функции передаются локальные копии значений переменных Передаются как ссылки, т.е. вызывающей функции передаются адреса размещения соответствующих объектов в оперативной памяти
Существует ли возможность переопределить встроенный метод Object.Finalize() Нет. Структурные типы никогда не размещаются в куче, поэтому к ним нельза применить функцию освобождения памяти Да, но не напрямую (подробнее дальше...)
Можно ли определить конструкторы для этого типа Да, но конструктор по умолчанию зарезервирован, поэтому все определяемые конструкторы должны иметь параметры Да, сколько угодно, лишь бы они различались сигнатурой
Когда происходит разрушение переменной данного типа Когда происходит выход из области видимости Во время процесса сборки мусора в управляемой куче с помощью механизма Garbare Collector (сборщик мусора)
Можно ли напрямую инициализировать данные при объявлении типа Нет Да
Можно ли перегружать конструктор по умолчанию Нет, он зарезервирован за системой Да, и обязательно, если определен хоть один общий конструктор

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

Продемонстрируем сказанное простым примером.

  • Внесите следующие изменения в файл Test.cs консольного приложения C# с именем Test
using System;
  
namespace Test
{
//**********************************************************
struct PERSON // Структурный тип
{
private string Name;// Прямая инициализация не допускается
private int Age;// Прямая инициализация не допускается
//****************************************
// Объявления перегружаемых конструкторов
// Конструктор по умолчанию перегружать нельзя!!!
//****************************************
public PERSON(string name, int age)
{
  Name = name; Age = age;
}
//****************************************
public PERSON(int age, string name)
{
  Age = age; Name = name; 
}
  
//****************************************
// Сервисы
//****************************************
public string GetName()
{
  return Name;
}
//****************************************
public void SetName(string name)
{
  Name = name;
}
//****************************************
public int GetAge()
{
  return Age;
}
//****************************************
public void SetAge(int age)
{
  Age = age;
}
}
  
//**********************************************************
class Person // Ссылочный тип
{
private string Name = "Иванов";// Прямая инициализация допустима
private int Age = 20;// Прямая инициализация допустима
//****************************************
// Перегружаемые конструкторы
// Конструктор по умолчанию перегружать можно
//****************************************
public Person(){}
//****************************************
public Person(string name, int age)
{
  Name = name; Age = age;
}
//****************************************
public Person(int age, string name)
{
  Age = age; Name = name; 
}
//****************************************
public Person(string name)
{
  Name = name;
}
//****************************************
public Person(int age)
{
  Age = age;
}

//****************************************
// Сервисы
//****************************************
public string GetName()
{
  return Name;
}
//****************************************
public void SetName(string name)
{
  Name = name;
}
//****************************************
public int GetAge()
{
  return Age;
}
//****************************************
public void SetAge(int age)
{
  Age = age;
}
}
  
class ValRefClass
{
  static void Main()
  {
  // Создаем объект на стеке
  PERSON petrov = new PERSON("Денис 
    Петров", 23);// Общий конструктор
  PERSON empty = new PERSON();// Другой объект с конструктором по умолчанию
           // Круглые скобки обязательны
  
  // Создаем объект на куче
  Person sergey = new Person(21, "
    Сережа Жук!");
  Person ivanov = new Person(); // Создание с конструктором по умолчанию 
  
  // Вывод из стека
  Console.WriteLine("Содержимое объектов на стеке");
  Console.WriteLine("Объект petrov: Имя={0}; 
    возраст={1}",petrov.GetName(), petrov.GetAge());
  Console.WriteLine("Объект empty: Имя={0}; 
    возраст={1}",empty.GetName(), empty.GetAge());
  
  Console.WriteLine("\n");
  // Вывод из кучи
  Console.WriteLine("Содержимое объектов на куче");
  Console.WriteLine("Объект sergey: Имя={0}; 
    возраст={1}",sergey.GetName(), sergey.GetAge());
  Console.WriteLine("Объект ivanov: Имя={0}; 
    возраст={1}",ivanov.GetName(), ivanov.GetAge());
  
  while(true);
  }
  }
}
Листинг 20.13 . Использование структурных и ссылочных типов в файле Test.cs

Результаты экрана

Содержимое объектов на стеке
Объект petrov: Имя=Денис Петров; возраст=23 
Объект empty: Имя=; возраст=0
  
Содержимое объектов на куче
Объект sergey: Имя=Сережа Жук!; возраст=21 
Объект ivanov: Имя=Иванов; возраст=20
< Лекция 19 || Лекция 20: 1234567891011
Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

Срок действия этого кода проверки уже истек. Проверьте, правильно ли введен код. У вас осталось две попытки. Вы также можете выбрать другой способ проверки или предоставить соответствующие документы, подтверждающие ваш академический статус.

 

Как активировать код?