Опубликован: 19.02.2009 | Доступ: свободный | Студентов: 3054 / 785 | Оценка: 4.35 / 4.11 | Длительность: 16:28:00
ISBN: 978-5-94774-401-9
Лекция 13:

Классы

Конструкторы

Конструктор предназначен для инициализации объекта. Конструкторы делятся на конструкторы класса (для статических классов) и конструкторы экземпляра класса (всех остальных классов).

Конструкторы экземпляра

Конструктор экземпляра вызывается автоматически при создании объекта класса с помощью операции new. Имя конструктора совпадает с именем класса. Рассмотрим основные свойства конструкторов:

  1. Конструктор не возвращает значение, даже типа void.
  2. Класс может иметь несколько конструкторов с разными параметрами для разных видов инициализации.
  3. Если программист не указал ни одного конструктора или какие-то поля не были инициализированы, полям значимых типов присваивается нуль, полям ссылочных типов - значение null.

До сих пор мы задавали начальные значения полей класса при описании класса. Это удобно в том случае, когда для всех экземпляров класса начальные значения некоторого поля одинаковы. Если же при создании объектов требуется присваивать полю разные значения, это следует делать с помощью явного задания конструктора. В следующем примере добавлен конструктор и метод Print для вывода информации об объекте:

class Circle
 {
  public int x;
  public int y;
  public int radius;
  public const double pi = 3.14;
  public static string name = "Окружность";
  public Circle(int x, int y, int r)//конструктор
  {
   this.x = x;
   this.y = y;
   radius = r;
  }
  public void Print()
  {
   Console.Write(name);
   Console.WriteLine(" с центром в точке ({0},{1}) и радиусом {2}", x, y, radius); 
   Console.WriteLine();
  }
 }

 class Program
 {
  static void Main()
  {
   Circle a = new Circle(0, 0, 1);   //вызов конструктора 
   a.Print();
   Circle b=new Circle(10, 10, 5);//вызов конструктора 
   b.Print();
  }
 }

Часто бывает удобно задать в классе несколько конструкторов, чтобы обеспечить возможность инициализации объектов разными способами. Для этого конструкторы должны иметь разные сигнатуры. Рассмотрим это на примере:

class Circle
 {
  public int x;
  public int y;
  public int radius;
  public const double pi = 3.14;
  public static string name = "Окружность";
  public Circle(int x, int y, int r)//конструктор 1
  {
   this.x = x;
   this.y = y;
   radius = r;
  }
  public Circle(int r)//конструктор 2
  {
   radius = r;
  }
  public void Print()
  {
   Console.Write(name);
   Console.WriteLine(" с центром в точке ({0},{1}) и радиусом {2}", x, y, radius); 
   Console.WriteLine();
  }
 }

 class Program
 {
  static void Main()
  {
   Circle a = new Circle(0, 0, 1);   //вызов конструктора 1
   a.Print();
   Circle b=new Circle(5);//вызов конструктора 2
   b.Print();
  }
 }

Обратите внимание на то, что в конструктое 2 не были инициализированы поля x, y, поэтому им присваивается значение 0.

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

public Circle(int x, int y, int r):this(r)  //конструктор 1
{
 this.x = x;
 this.y = y;
}

public Circle(int r)    //конструктор 2
{
 radius = r;
}

Конструкция, находящаяся после двоеточия, называется инициализатором, то есть тем кодом, который исполняется до начала выполнения тела конструктора. В нашем случае конструктор 1 до выполнения своего кода вызывает конструктор 2.

Конструкторы класса

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

Чтобы подчеркнуть этот факт, в первой версии С# для статических классов создавали два конструктора, один - пустой закрытый (private) конструктор, второй - статический конструктор, не имеющий параметров. Первый конструктор предотвращал попытки создания экземпляров класса. Второй конструктор автоматически вызывается системой до первого обращения к любому элементу статического класса, выполняя необходимые действия по инициализации. Вышесказанное отражено в следующем примере:

class Demo
{
  static int a;
  static int b;
  private Demo(){}   //закрытый конструктор
  static Demo()       //статический конструктор
  {
   a=10;
   b=2;
  }
  public static void Print ()
  {
   Console.WriteLine("{0}+{1}={2}",a,b,a+b);
   Console.WriteLine("{0}*{1}={2}",a,b,a*b);
   Console.WriteLine("{0}-{1}={2}",a,b,a-b);
  }      
}

class Program
{
 static void Main()
 {
  //Demo S=new Demo();    //ошибка содать экземпляр класса нельзя
  Demo.Print();    
 }
}

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

static class Demo
{
  static int a=20;
  static int b=10;
  public static void Print ()
  {
    Console.WriteLine("{0}+{1}={2}",a,b,a+b);
    Console.WriteLine("{0}*{1}={2}",a,b,a*b);
    Console.WriteLine("{0}-{1}={2}",a,b,a-b);
  }      
}

class Program
{
  static void Main()
  {
    Demo.Print();    
  }
}
Свойства

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

Синтаксис свойства:

[атрибуты] [спецификаторы] тип имя_свойства
{
   [get код_доступа]
   [set код_доступа]
}

Значения спецификаторов для свойств и методов аналогичны. Чаще всего свойства объявляются как открытые (со спецификатором public ).

Код доступа представляет собой блоки операторов, которые выполняются при получении ( get ) или установке ( set ) свойства. Может отсутствовать либо часть get, либо set, но не обе одновременно. Если отсутствует часть set, то свойство доступно только для чтения. Если отсутствует часть get, то свойство доступно только для записи.

Рассмотрим пример работы со свойствами, обращая внимание на то, что синтаксически чтение и запись свойства выглядят почти как методы. При этом get должен содержать оператор return, возвращающий выражение, для типа которого должно существовать неявное преобразование к типу свойства.

class Circle
 {
  //закрытые поля
  int x;
  int y;
  int radius;
  public static string name = "Окружность";
  public Circle(int x, int y, int r):this(r)//конструктор 1
  {
   this.x = x;
   this.y = y;
  }
  public Circle(int r)//конструктор 2
  {
   radius = r;
  }
  public void Print()
  {
   Console.Write(name);
   Console.WriteLine(" с центром в точке ({0},{1}) и радиусом {2}", x, y, radius); 
   Console.WriteLine();
  }
  public int X //свойство для обращения к полю x
  {
   get 
   { 
    return x;
   }
   set 
   {
    x = value;
   }
  }
  public int Y //свойство для обращения к полю y
  {
   get 
   { 
    return y;
   }
   set 
   {
    y = value;
   }
  }
  public int R //свойство для обращения к полю radius
  {
   get 
   { 
    return radius;
   }
   set 
   {
    radius = value;
   }
  }
  public double P //свойство только для чтения
  {
   get 
   { 
    return 2* Math.PI *radius;
   }
   
  }
  public double S //свойство только для чтения
  {
   get 
   { 
    return Math.PI *radius*radius;
   }
   
  }
 }

 class Program
 {
  static void Main()
  {
   Circle a = new Circle(0, 0, 1);  //вызов конструктора
   a.Print();
   //установка новых значений
   a.X=1;
   a.Y=1;
   a.R=10;
   //a.S=100; //ошибка - свойство доступно только для чтения
   Console.WriteLine("центр=({0},{1}) 
                         радиус={2} 
                         периметр={3:f2} 
                         площадь={4:f2}", 
       a.X, a.Y, a.R, a.P, a.S);
  }
 }
13.1.
"Один класс - один файл",

С добавлением новых классов в программу резко увеличивается ее размер, что затрудняет ее прочтение. Поэтому следует руководствоваться одним простым принципом "один класс - один файл". Для того чтобы создать новый файл для класса Circle выполним следующие действия:

  1. В окне Solution Explorer щелкните правой кнопкой на имени проекта Hello (на рисунке выделен жирным).

    13_02

  2. Выполните команду Add/Add Class…

    13_03

    В поле Name напишите Сircle.cs и нажмите кнопку Add.

    Теперь окно Solution Explorer выглядит следующим образом:

    13_04

    А файл Circle.cs выглядит следующим образом:

    using System;
    
    namespace ConsoleApplication1
    {
     public class Circle
     {
      public Circle()
      {
       //
       // TODO: Add constructor logic here
       //
      }
     }
    }
  3. Замените namespace ConsoleApplication1 на namespace MyProgram, для того чтобы идентификаторы файлов Program.cs и Circle.cs были определены в одном пространстве имен.
  4. Перенесите класс Circle из файла Program.cs в файл Circle.cs.
  5. Теперь запустите программу и посмотрите, что она делает.