1629295407-c61bfe4caba98380ea3e7cdae6295416 (846200), страница 42
Текст из файла (страница 42)
Подробнее о методах и классах219Начальное значение переменной StaticDemo.val равно 100Значение переменной StaticDemo.val равно 8StaticDemo.valDiv2() : 4Как видно по результатам выполнения программы, static-переменнаяинициализируется в начале ее работы, т.е. еще до создания объекта класса, в котором онаопределяется.На static-методы накладывается ряд ограничений.1. static-метод не имеет ссылки this,2. static-метод может напрямую вызывать только другие static-методы.
Он неможет напрямую вызывать метод экземпляра своего класса. Дело в том, чтометоды экземпляров работают с конкретными экземплярами класса, чего нескажешь о static-методах.3. static-метод должен получать прямой доступ только к static-данным. Он неможет напрямую использовать переменные экземпляров, поскольку не работает сэкземплярами класса.Например, в следующем классе static-метод valDivDenom() недопустим:class StaticError {int denom =3; // обычная переменная экземпляраstatic int val = 1024; // статическая переменная}/* Ошибка! Внутри статического метода прямой доступк нестатической переменной недопустим.
*/static int valDivDenom() {return val/denom; // Инструкция не скомпилируется!}Здесь denom — обычная переменная экземпляра, к которой невозможно получитьдоступ внутри статического метода. Однако с использованием переменной val проблемнет, поскольку это static-переменная.Аналогичная проблема возникает при попытке вызвать нестатический метод из staticметода того же класса. Вот пример:using System;class AnotherStaticError { // Нестатический метод.void nonStaticMeth() {Console.WriteLine("Внутри метода nonStaticMeth().");}}/* Ошибка! Внутри статического метода нельзя напрямуювызвать нестатический метод.
*/static void staticMeth() {nonStaticMeth(); // Инструкция не скомпилируется!}В этом случае попытка вызвать нестатический метод (т.е. метод экземпляра) изстатического метода приведет к ошибке компиляции.Важно понимать, что static-метод может вызывать методы экземпляров иполучать доступ к переменным экземпляров своего класса, но должен делать это черезобъект класса. Другими словами, он не может использовать обычные члены класса безуказания конкретного объекта. Например, этот фрагмент программы совершеннокорректен:220Часть I.
Язык C#class MyClass {// Нестатический метод.void nonStaticMeth() {Console.WriteLine("Внутри метода nonStaticMeth().");}}/* Внутри статического метода можно вызватьнестатический метод, использовав ссылку на объект. */public static void staticMeth(MyClass ob) {ob.nonStaticMeth(); // Здесь все в порядке.}Поскольку static-поля не зависят от конкретного объекта, они используются приобработке информации, применимой ко всему классу.
Рассмотрим пример такой ситуации.В следующей программе используется static-поле для обработки счетчика числасуществующих объектов.// Использование static-поля для подсчета экземпляров класса.using System;class CountInst {static int count = 0;// Инкрементируем счетчик при создании объекта.public CountInst() {count++;}}// Декрементируем счетчик при разрушении объекта.~CountInst() {count--;}public static int getcount() {return count;}class CountDemo {public static void Main() {CountInst ob;}}for(int i=0; i < 10; i++) {ob = new CountInst();Console.WriteLine("Текущее содержимое счетчика: " +CountInst.getcount());}Результаты выполнения этой программы выглядят так:Текущее содержимое счетчика: 1Текущее содержимое счетчика: 2Текущее содержимое счетчика: 3Текущее содержимое счетчика: 4Текущее содержимое счетчика: 5Глава 8.
Подробнее о методах и классах221Текущее содержимое счетчика: 6Текущее содержимое счетчика: 7Текущее содержимое счетчика: 8Текущее содержимое счетчика: 9Текущее содержимое счетчика: 10Каждый раз, когда создается объект типа CountInst, static-поле countинкрементируется. И каждый раз, когда объект типа CountInst разрушается, staticполе count декрементируется. Таким образом, статическая переменная count всегдасодержит количество объектов, существующих в данный момент. Это возможно толькоблагодаря использованию статического поля.
Переменная экземпляра не в состояниисправиться с такой задачей, поскольку подсчет экземпляров класса связан с классом вцелом, а не с конкретным его экземпляром.А вот еще один пример использования static-членов класса. Выше в этой главебыло показано, как использовать “фабрику” класса для создания объектов. В том примере вкачестве генератора объектов класса выступал нестатический метод, а это значит, что егоможно вызывать только через объектную ссылку, т.е. требовалось создать объект классалишь для того, чтобы получить возможность вызвать метод генератора объектов.
Поэтомулучше реализовать “фабрику” класса, используя static-метод, который позволяетобращаться к нему, не создавая ненужного объекта. Ниже приводится пример реализации“фабрики” класса, переписанный с учетом этого усовершенствования.// Создание статической "фабрики" класса.using System;class MyClass {int a, b;// Создаем "фабрику" для класса MyClass.static public MyClass factory(int i, int j) {MyClass t = new MyClass();t.a = i;t.b = j;}}return t; // Метод возвращает объект.public void show() {Console.WriteLine("а и b: " + a + " " + b);}class MakeObjects {public static void Main() {int i, j;// Генерируем объекты с помощью "фабрики" класса.for(i=0, j=10; i < 10; i++, j--) {MyClass ob = MyClass.factory(i, j);// Получение// объекта.ob.show();}222Часть I.
Язык C#}}Console.WriteLine();В этой версии программы метод factory() вызывается посредством указанияимени класса:MyClass ob = MyClass.factory(i, j); // Получение объекта.Этот пример показывает, что нет необходимости создавать объект класса MyClassдо использования “фабрики” класса.Статические конструкторыКонструктор класса также можно объявить статическим. Статический конструкторобычно используется для инициализации атрибутов, которые применяются к классу вцелом, а не к конкретному его экземпляру. Таким образом, статический конструкторслужит для инициализации аспектов класса до создания объектов этого класса.
Рассмотримпростой пример.// Использование статического конструктора.using System;class Cons {public static int alpha;public int beta;// Статический конструктор.static Cons() {alpha = 99;Console.WriteLine("Внутри статического конструктора.");}}// Конструктор экземпляра.public Cons() {beta = 100;Console.WriteLine("Внутри конструктора экземпляра.");}class ConsDemo {public static void Main() {Cons ob = new Cons();Console.WriteLine("Cons.alpha: " + Cons.alpha);Console.WriteLine("ob.beta: " + ob.beta);}}Вот результаты выполнения этой программы:Внутри статического конструктора.Внутри конструктора экземпляра.Cons.alpha: 99ob.beta: 100Обратите внимание на то, что статический конструктор вызывается автоматически,причем до вызова конструктора экземпляра.
В общем случае static-конструктор будетвыполнен до любого конструктора экземпляра. Кроме того, static-конструкторы должныбыть закрытыми, и их не может вызвать ваша программа.Глава 8. Подробнее о методах и классах223Полныйсправочник поГлава 9Перегрузка операторовЯзык C# позволяет определить значение оператора относительно создаваемого класса.Этот процесс называется перегрузкой операторов. Перегружая оператор, вырасширяете его использование для класса.
Результат действия оператора полностьюнаходится в ваших руках, и может быть разным при переходе от класса к классу. Например,класс, который определяет связный список, может использовать оператор “+” длядобавления объектов в список. Класс, который реализует стек, может использоватьоператор “+” для занесения объекта в стек. А какой-то другой класс может использоватьэтот оператор иным способом.При перегрузке оператора ни одно из его исходных значений не теряется. Перегрузкуоператора можно расценивать как введение новой операции для класса.
Следовательно,перегрузка оператора “+”, например, для обработки связного списка (в качестве операторасложения) не изменяет его значение применительно к целым числам.Главное достоинство перегрузки операторов состоит в том, что она позволяетбесшовно интегрировать новый тип класса со средой программирования. Этарасширяемость типов — важная составляющая мощи таких объектно-ориентированныхязыков программирования, как C#. Если для класса определены некоторые операторы, выможете оперировать объектами этого класса, используя обычный C#-синтаксис выражений.Более того, вы можете использовать в выражениях объект, включающий другие типыданных. Перегрузка операторов — одно из самых мощных средств языка C#.Основы перегрузки операторовПерегрузка операторов тесно связана с перегрузкой методов. Для перегрузкиоператоров используется ключевое слово operator, позволяющее создать операторныйметод, который определяет действие оператора, связанное с его классом.Существует две формы методов operator: одна используется для унарных операторов,а другая — для бинарных.
Общий же формат (для обоих случаев) таков:// Общий формат перегрузки для унарного оператора.public static тип_возврата operator ор(тип_параметра операнд){// операции}// Общий формат перегрузки для бинарного оператора.public static тип_возврата operator ор(тип_параметра1 операнд1, тип_параметра2 операнд2){// операции}Здесь элемент op - это оператор (например “+” или “/”), который перегружается.Элемент тип_возврата — это тип значения, возвращаемого при выполнении заданнойоперации.
Несмотря на то что можно выбрать любой тип, тип возвращаемого значениячаще всего будет совпадать с типом класса, для которого этот оператор перегружается.Такая корреляция облегчает использование перегруженного оператора в выражениях. Дляунарных операторов операнд передается в элементе операнд, а для бинарных — вэлементах операнд1 и операнд2.Для унарных операторов тип операнда должен совпадать с классом, для которогоопределен оператор. Что касается бинарных операторов, то тип хотя бы одного операндаГлава 9.
Перегрузка операторов225должен совпадать с соответствующим классом. Таким образом, C#-операторы нельзяперегружать для классов, не созданных вами. Например, вы не можете перегрузитьоператор “+” для типов int или string.И последнее: параметры операторов не должны использовать модификатор refили out.Перегрузка бинарных операторовЧтобы разобраться, как работает перегрузка операторов, начнем с примера, в которомперегружаются два бинарных оператора — “+” и “-“. В следующей программе создаетсякласс ThreeD поддержки координат объекта в трехмерном пространстве.