Структуры в языке C#

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

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

Для разрешения подобных затруднений в C# предусмотрена структура, которая подобна классу, но относится к типу значений, а не к ссылочному типу данных.

Структуры отличаются от классов тем, как они сохраняются в памяти и как к ним осуществляется доступ (для начала – краткая формула «классы — это ссылочные типы, размещаемые в куче, структуры — типы значений, размещаемые в стеке»). Эта формула не совсем точная, пояснения см. позже. Структуры отличаются от классов некоторыми свойствами (например, структуры не поддерживают наследование).

Из соображений производительности следует использовать структуры для небольших типов данных. Однако в отношении синтаксиса структуры очень похожи на классы.

Первое и очевидное отличие состоит в том, что при их объявлении используется ключевое слово struct вместо class. Ниже приведена общая форма объявления структуры:

struct имя
{
объявления членов
}
где имя обозначает конкретное имя структуры.

Как у класса, так и у каждой структуры имеются свои члены: методы, поля, индексаторы, свойства, операторные методы и события. Так в структуре Double (библиотека System) определено 6 констант, 6 операторов отношения, 19 методов, 5 интерфейсов (проверьте, используя интеллектуальную подсказку). Не многовато ли для вещественного числа, как вы думаете?

В структурах допускается также определять конструкторы. В то же время для структуры нельзя определить конструктор, используемый по умолчанию (без параметров), который определяется для всех структур автоматически и не подлежит изменению. Такой конструктор инициализирует поля структуры значениями, задаваемыми по умолчанию (false для типа bool, 0 – для чисел). А поскольку структуры в отличие от классов не поддерживают наследование, то их члены нельзя указывать как abstract, virtual  или  protected.

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

// Пример: Структуры
using System;
namespace ConsoleApplication1
{
  // Создадим структуру вне класса Program
  struct student
  {
     public string name;    // поле - имя
     public byte age;       // поле - возраст
     public student(string Name, byte Age)  // конструктор
     {
        this.name = Name;
        age = Age;   // указатель this можно не добавлять
     }
     public void WriteUserInfo()   // метод для вывода
     {
        Console.WriteLine("Имя: {0}, возраст: {1}", name, age);
     } 
  }        // конец объявления структуры
  class Program
  {
     static void Main()
     {
        student stud1;    // Без конструктора
        stud1.name = "Петр";
        stud1.age = 18;
        Console.Write("студент1: ");
        stud1.WriteUserInfo();
        student stud2 = new student("Елена", 17);  // с конструктором
        Console.Write("студент2: ");
        stud2.WriteUserInfo();
 // Отличие структур от классов – в передаче по значению
        stud1 = stud2;   // пересылка
        stud2.name = "Наталья";  // изменение полей структуры
        stud2.age = 19;
        Console.Write("\nстудент1: ");
        stud1.WriteUserInfo();
        Console.Write("студент2: ");
        stud2.WriteUserInfo();
        Console.ReadKey();
     }
  }
}

Результат выполнения программы:

34
Исследуем программу. Закомментируем вторую строку в методе Main():
// stud1.name = «Петр»;
При запуске программы получим сообщение об ошибке:
Использование локальной переменной «stud1», которой не присвоено значение.
Хотя структура stud1 объявлена, но полю name никакого значения не присвоено, что приводит к ошибке. Такая же ошибка возникнет, если мы станем использовать переменную целого типа, которая объявлена, но не инициализирована.
Такой вот жесткий контроль компилятора за нашими действиями!

Проверим, как работает конструктор без параметров. Первую строку метода Main() заменим на
student stud1 = new student();  
Вторую и третью строку – закомментируем. Тогда при выводе результата в 1 строке получим:
студент 1: Имя: , возраст: 0
Следовательно, полю name была присвоена пустая строка, а полю возраст – число 0.

Проверим, как работает защита данных. В второй строке описания структуры student удалим модификатор поля public, т.е. получим byte age;
При запуске программы получим два сообщения об ошибке:
«ConsoleApplication1.student.age» недоступен из-за его уровня защиты
в операторах stud1.age = 18; и  stud2.age = 19;

Это означает, что так как по умолчанию поле age является полем private, то оно не может быть изменено в другом классе с помощью оператора присваивания, однако оно доступно методам класса student, объявленным как public. С учетом этих результатов, программа может быть записана так:

using System;
namespace ConsoleApplication1
{
   struct student
   {
      string name;
      byte age;
      public student(string Name, byte Age)
      {
         name = Name;
         age = Age;
      }
      public void WriteUserInfo()
      {
         Console.WriteLine("Имя: {0}, возраст: {1}", name, age);
      }
   }
   class Program
   {
      static void Main()
      {
         student stud1 = new student("Петр",18);
         stud1.WriteUserInfo();
         student stud2 = new student("Елена", 17);
         stud2.WriteUserInfo();
         Console.ReadKey();
      }
   }
}

Здесь поля объектов stud1 и stud2 структуры student являются private-полями, а метод и конструктор объявлены как public.

А теперь ВНИМАНИЕ! Ключевая идея — Создание сложных конструкций данных, используя один тип данных, как элемент другой конструкции. В статье про типы данных мы выяснили, что все встроенные типы, относящиеся к типам значений (их еще называют простыми типами данных), являются структурами, хотя фактически каждая из них имеет одно поле, содержащее значение числа, символа или булевой переменной.

Структура позволяет объединять несколько различных по типу элементов под одним именем. Более того можно использовать неоднократное вложение одних типов данных в другие, используя структуры.

Для понимания этой идеи рассмотрим пример. Прототипом для него являются структуры из библиотеки System. Drawing:  Point, Size и Rectangle.

Известно, что точка на экране задается парой целых чисел (X,Y) в пикселях. Объединим эти два числа в структуру точка. Установим максимальную защиту (private – по умолчанию) для этих полей. Добавим конструктор с параметрами точка(x,y) для инициализации объектов структуры точка, а также метод Get() для извлечения полей в строковом формате.

Размер прямоугольника на экране также задается парой чисел: шириной и высотой. Соответственно объявим структуру размер с полями (W,H). Добавим аналогичный конструктор с параметрами размер(w,h) для инициализации объектов структуры размер, а также метод Get() для извлечения полей в строковом формате.

Прямоугольник на экране определяется координатами верхнего левого угла (точка) и размером изображения (размер). Поэтому третью структуру построим на основе первых двух, назовем ее Rect (сокращенно от «прямоугольник»), полями которой будут объекты первых двух структур: точка P и размер S. Эти поля также будут защищенными (private – по умолчанию). Добавим в эту структуру конструктор с параметрами public Rect(int x, int y, int w, int h) и три метода: Get() для извлечения информации о прямоугольнике, Get_P() и Get_S для извлечения координат и размеров по отдельности.

В методе Main( ) последовательно инициализируем объект Rect r и покажем работу методов всех этих трех структур.

using System;
namespace прямоугольник
{
   struct точка
   {
      int X;
      int Y;
      public точка (int x, int y)
      {
         X = x;
         Y = y;
      }
      public string Get()
      {
         return X.ToString()+","+Y.ToString();
      }
   }
   struct размер
   {
      int W;
      int H;
      public размер(int w, int h)
      {
         W = w;
         H = h;
      }
      public string Get()
      {
         return W.ToString() + "x" + H.ToString();
      }
   }
   struct Rect
   {
      точка P;
      размер S;
      public Rect(int x, int y, int w, int h)
      {
         P = new точка(x,y);
         S = new размер(w,h);
      }
      public void Get()
      {
         Console.Write("Параметры прямоугольника:  ");
         Console.Write("Координаты: ({0})", P.Get());
         Console.Write("   Размеры - {0}", S.Get());
         Console.WriteLine();
      }
      public точка Get_P()
      {
         return P;
      }
      public размер Get_S()
      {
         return S;
      }
   }
   class Program
   {
      static void Main(string[] args)
      {
         Rect r = new Rect(100,50,30,40);
         r.Get();
         точка q = r.Get_P();    // т.к. r.P; - ошибка
         string z = "Координаты: (" + q.Get()+")";
         Console.WriteLine(z);
         размер d = r.Get_S();   // т.к. r.S; - ошибка
         z = "Размеры: " + d.Get();
         Console.WriteLine(z);
         Console.ReadKey();
      }
   }
}

Результат:

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

Далее рассмотрим отличия структур от классов.


NEW: Наш Чат, в котором вы можете обсудить любые вопросы, идеи, поделиться опытом или связаться с администраторами.


Помощь проекту:

Понравилась статья? Поделиться с друзьями:
0 0 голоса
Рейтинг статьи
Подписаться
Уведомить о
1 Комментарий
Новые
Старые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии

Спасибо!

1
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x
()
x