Опубликован: 23.07.2006 | Доступ: свободный | Студентов: 2213 / 889 | Оценка: 4.28 / 4.17 | Длительность: 21:37:00
Специальности: Системный архитектор
Лекция 2:

Обзор языка C#

< Лекция 1 || Лекция 2: 123456 || Лекция 3 >

Классы

Классы - это основной способ организации данных в C#; любая исполняемая программа, написанная на этом языке, должна представлять собой класс (так что C# представляет собой "настоящий" объектно-ориентированный язык, в отличие, скажем, от С++, в котором использование объектов возможно, но необязательно).

На следующих слайдах мы рассмотрим языковые возможности, связанные с классами:

  • Различные модификаторы доступа, которые могут быть использованы в классах
  • Конструкторы и деструкторы в классах
  • Методы объектов и их параметры
  • Свойства классов (properties) и способы доступа к ним
  • Индексаторы
  • События и представители

Модификаторы

Одним из важных принципов объектно-ориентированного подхода к программированию является инкапсуляция данных: считается, что внутреннее устройство класса и конкретная реализация его методов должны быть неизвестны внешним потребителям. Для выражения различных уровней доступности элементов программы в C# существует обширный набор модификаторов, позволяющий утверждать, что на данный момент C# обладает наиболее развитыми средствами поддержки инкапсуляции. Например, поля могут быть описаны следующим образом:

  • public (данный элемент класса доступен всем внешним потребителям)
  • protected (к такому элементу класса могут обращаться только классы, унаследованные от данного)
  • private (элемент недоступен за пределами описания класса, т.е. недоступен даже потомкам данного класса; этот модификатор ставится по умолчанию)
  • internal (элемент доступен только для классов, определенных в той же сборке, что и данный класс)

Кроме того, существуют модификаторы, изменяющие поведение элементов класса:

  • const (свидетельствует, что данная переменная не может быть изменена)
  • event (указывает, что данный элемент описывает событие; о событиях см. ниже)
  • extern (обычно описывает метод с внешней реализацией, чаще всего, импортированный из Win32)
  • override (используется в случае новой реализации виртуального метода)
  • readonly (описывает переменные, которые могут получить значение только при самом описании или в конструкторе класса)
  • static (указывает, что данный элемент принадлежит типу объекта, а не конкретному экземпляру)
  • virtual (описывает метод, который может быть переопределен в потомках класса)

Наконец, класс в целом может иметь следующие дополнительные модификаторы:

  • abstract (обозначает, что данный класс не может быть использован самостоятельно, а является только базой для классов-потомков)
  • sealed (от таких классов нельзя наследовать; кстати, по очевидным соображениям комбинация модификаторов abstract sealed запрещена)

Конструкторы и деструкторы

class TestClass {
  public TestClass() { … }
  public ~TestClass () { ReleaseResources(); }
  public ReleaseResources() { 
    // closing connections, releasing system resources etc.
  }

Конструкторы/деструкторы не возвращают значений.

Для статических классов конструкторы могут быть закрытыми ( private ).

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

public Matrix() // implements default constructor
{
   for (int i=0; i<n; i++)
      for (int j=0; j<n; j++)
         elements[i,j] = 0; 
}

public Matrix (int val) // implements constructor with one parameter
{
   for (int i=0; i<n; i++)
      for (int j=0; j<n; j++)
         elements[i,j] = val;
}

В C# нет деструкторов в привычном понимании этого слова, т.е. объекту не может быть приписан специальный метод, физически уничтожающих объект по явному запросу пользователя. Дело в том, что за освобождение памяти в .NET отвечает механизм сборки мусора и потому явное уничтожение объектов в C# не предусмотрено. Предусмотрены только так называемые завершители (finalizers), которые вызываются сборщиком мусора непосредственно перед уничтожением объекта. Но так как невозможно предсказать когда произойдет сборка мусора (и даже произойдет ли вообще), более надежным способом считается выделение завершающих действий в отдельный метод с именем Close или Dispose. Этот метод может вызывать как завершитель, так и сам пользователь.

Методы и их параметры

Все активные действия программ на C# выполняются в методах классов. Естественно, эти методы могут получать на вход параметры и выдавать значения. При передаче параметров в C# необходимо явно указывать способ передачи - по значению или по ссылке; в последнем случае переменной должно предшествовать ключевое слово ref . Кроме того, создатели C# предусмотрели возможность для возвращения более чем одного значения из метода - для этого помимо явного возвращаемого значения метода, необходимо описать один или несколько параметров метода с ключевым словом out. Компилятор C# проверяет, что ref -параметры инициализируются перед вызовом метода, а также, что out-параметры получают значение до выхода из метода.

С точки зрения перегрузки методов, отличительной особенностью C# является то, что методы по умолчанию не являются виртуальными. Это сделано для того, чтобы избежать ошибок, связанных со случайным переопределением унаследованных функций. Кроме того, в C# есть два способа переопределения виртуального метода: при использовании ключевого слова override базовый метод становится недоступным, а при использовании ключевого слова new базовый метод все еще может быть вызван путем явного приведения к типу базового класса, как в следующем примере:

class BaseClass {
  public void TestMethod() {
    Console.WriteLine ("BaseClass.TestMethod()");
  }
}
class DerivedClass : BaseClass {
  new public void TestMethod() {
    Console.WriteLine ("DerivedClass.TestMethod()");
  }
}
...
DerivedClass test = new DerivedClass();
test.TestMethod(); // напечатает DerivedClass.TestMethod
((BaseClass)test).TestMethod(); // напечатает BaseClass.TestMethod

Свойства класса и доступ к ним

  • Поля: public int stateOfVeryCriticalResource ;
  • Свойства:
    private int m_stateOfVeryCriticalResource;
    public int stateOfVeryCriticalResource {
      get { if (IsAllowedUser()) 
               return m_stateOfVeryCriticalResource; }
      set { if (IsAdmin()) 
               m_stateOfVeryCriticalResource = value; }
    }
    ...
    stateOfVeryCriticalResource = vcrCompletelyScrewedUp;

В объектно-ориентированном программировании считается "хорошим тоном" организовывать доступ к данным через специальные методы доступа get и set . Однако до недавнего времени эта рекомендация, к сожалению, совершенно не поддерживалась языками программирования. В C# такая языковая возможность наконец-то появилась. Теперь обычное описание поля можно дополнить методами доступа get и set и тогда при любом чтении поля или при присваивании ему значения будет обязательно выполняться функциональность, записанная в этих методах доступа (обратите внимание, что в методе set используется ключевое слово value ).

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

private int m_AgeOfClient;
public int AgeOfClient {
   get { if (AccessToPersonalInfoAllowed()) return m_AgeOfClient; }
   set { if (value > 0 & & value <= 120) 
             m_AgeOfClient = value;
         else
             MessageBox.Show("This client is not recommended for insurance"); 
       }
}

Второй случай возникает, например, в следующей программе:

private int m_stateOfVeryCriticalResource;
public int stateOfVeryCriticalResource {
  get { if (IsAllowedUser()) 
           return m_stateOfVeryCriticalResource; }
  set { if (IsAdmin()) 
           m_stateOfVeryCriticalResource = value; }
}
...
stateOfVeryCriticalResource = vcrCompletelyScrewedUp;
< Лекция 1 || Лекция 2: 123456 || Лекция 3 >