Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Объектно-ориентированное программирование.-6

.pdf
Скачиваний:
6
Добавлен:
05.02.2023
Размер:
4.5 Mб
Скачать

Один из примеров – метод Main. Среде CLR нужна универсальная точка входа в ваше приложение. Поскольку CLR не должна создавать экземпляры наших объектов автоматически (это прерогатива программиста), существует правило, требующее определить в одном из классов статический ме-

тод Main.

Некоторые члены класса обязаны быть статическими, в частности, перегруженные операторы (см. п. 4.7.3). Для других членов мы сами решаем – быть им статическими или нет. Если мы описываем член, формально принадлежащий классу, но не требующий реального объекта, то логично его сделать статическим. Например:

class MyClass1

{

public int Sqr(int x)

{

return x * x;

}

}

Здесь метод Sqr является методом экземпляра. Т.е. для его использования нужно создать экземпляр класса MyClass1:

MyClass1 cls1 = new MyClass1();

Console.WriteLine(cls1.Sqr(5));

Но зачем? Ведь данный метод не использует никакие другие члены экземпляра класса. Поэтому логичнее сделать так:

class MyClass2

{

public static int Sqr(int x)

{

return x * x;

}

}

Пользоваться таким методом проще:

Console.WriteLine(MyClass2.Sqr(5));

В следующем случае сделать методы статическими не получится:

class MyClass3

{

public int x = 0;

public int Sqr()

{

return x * x;

}

public static int Cube()

261

{

return x * x * x; // Ошибка

}

}

Здесь поле «x» является полем экземпляра, поэтому будет своим у каждого экземпляра класса. Ошибка связана с тем, что из одного же статического члена класса можно обращаться только к другим статическим членам. Из члена экземпляра можно получить доступ к любому члену класса.

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

class MyClass4

{

static public int Count = 0;

public MyClass4()

{

Count++;

}

}

Т.к. поле Count статическое, оно будет единым для всех классов. Даже если мы сами не знаем, сколько экземпляров класса MyClass4 было создано, в этом поле будет храниться точное значение:

MyClass4[] arr = new MyClass4[20];

Random rnd = new Random();

for (int i = 0; i < arr.Length; i++)

{

if (rnd.Next(2) == 0) arr[i] = new MyClass4();

}

Console.WriteLine("Экземпляров: {0}", MyClass4.Count);

У статических полей класса должно быть некоторое допустимое значение. Его можно задать при определении (как это сделано в примере выше). Иначе ему будет присвоено значение по умолчанию.

Если класс объявлен как статический, то:

1)Он может содержать только статические члены. Константы (const) также считаются статическими полями.

2)Для него запрещено указывать список наследования. Единственное исключение – класс System.Object. Неявно все статические классы наследуются от него, и можно это указать явно.

3)От него нельзя наследоваться. По этой причине в статическом классе нельзя использовать модификаторы доступа protected и protected internal. –

262

они имеют смысл только при наличии классов-потомков.

4) Нельзя создавать его экземпляры. Т.к. все его члены являются статическими, то создавать, в принципе, нечего. Конструктор, если он объявлен, никогда не будет вызван.

Пример:

static class MyStatClass1 { } // ОК

static class MyStatClass2 : MyStatClass1 { } // Ошибка

static class MyStatClass3 : Object // ОК

{

public int f1; // Ошибка public const int f2 = 1; // ОК

protected static int f3; // Ошибка internal static int f4 = 1; // ОК

static MyStatClass3()

{

Console.WriteLine("Мы этого сообщения не увидим...");

}

public enum ZZZ { Z1, Z2, Z3 }; public class ChildClass { }

}

class MyClass5 : MyStatClass1 {} // Ошибка

static int Main()

{

MyStatClass3 sc1 = new MyStatClass3(); // Ошибка MyStatClass3.ChildClass sc2 = new MyStatClass3.ChildClass(); // ОК

Console.WriteLine("Мы используем MyStatClass3, но его конструктор не вызывается");

Console.WriteLine(MyStatClass3.f2); return 0;

}

Пример: Samples\4.2\4_2_3_static.

4.2.4. Создание и удаление экземпляров класса

Переменную, имеющую тип класса, можно инициализировать ссылкой на новый или уже существующий объект:

class MyClass

{

public int x = 1;

}

static int Main()

{

MyClass cls1 = new MyClass(); MyClass cls2 = cls1;

263

cls1.x = 5; Console.WriteLine(cls2.x); // 5 return 0;

}

Если мы хотим, чтобы экземпляр cls2 был копией экземпляра cls1, а не ссылкой на него, нужно использовать структуры. Копирование же классов не предусмотрено. Хотя можно самостоятельно описать метод, копирующий класс:

class MyClass

{

public int x = 1;

public MyClass Copy()

{

MyClass cls = new MyClass(); cls.x = x;

return cls;

}

}

static int Main()

{

MyClass cls1 = new MyClass(); MyClass cls3 = cls1.Copy();

cls1.x = 5; Console.WriteLine(cls3.x); // 1 return 0;

}

Чтобы как-то систематизировать копирование классов (ведь методы копирования каждый разработчик может сделать по-своему), используется интерфейс ICloneable (см. п. 4.9.4.3).

Запрещено создавать экземпляры статических и абстрактных классов. Структуры, как мы уже отмечали, могут быть созданы и без использования оператора new (см. п. 3.1.2.6).

При описании операторов языка C# мы ничего не говорили об операторе удаления объектов delete. Дело в том, что такого оператора в языке C#, в отличие от языка C++, нет. Все объекты в управляемой динамической куче, на которые не осталось ссылок, удаляются сборщиком мусора автоматически.

Пример: Samples\4.2\4_2_4_constr.

264

4.2.5.Вложенные типы

Вклассе можно описывать новые типы данных – вложенные типы. Полным именем вложенного типа будет

<имя класса>.<имя вложенного типа>

Это могут быть:

перечисления (п. 3.1.2.5);

структуры (п. 3.1.2.6);

классы;

делегаты (§ 4.8);

интерфейсы (§ 4.9).

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

Пример:

class MyClass

{

protected enum MyEnum { }; public MyEnum field1; // Ошибка private MyEnum field2; // ОК

private struct MyStruct { }

public void F1(MyStruct st) { } // Ошибка private void F2(MyStruct st) { } // ОК

}

Пример: Samples\4.2\4_2_5_types.

265

§ 4.3. Описание полей класса

Здесь и далее при описании синтаксиса членов класса мы не будем упоминать атрибуты, с ними разберемся в § 5.4. Также не будем говорить о модификаторах, за исключением тех случаев, когда они оказывают существенное влияние на поведение члена класса. Доступные же для различных членов класса модификаторы перечислены выше.

4.3.1. Константы

Синтаксис:

<константа> :: [<атрибуты>] [<модификаторы>] const <оператор объявления>

Тип, указанный в объявлении константы, должен быть типом sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, string, перечисляемым или ссылочным типом. Таким образом, например, константа не может иметь тип структуры. Константа отличного от string ссылочного типа может инициализироваться только значением null.

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

Пример:

int a

= 1;

 

 

 

 

 

 

const

int

x =

1; //

ОК

const

int

y =

x

*

x

+ x; // ОК

const

int

b =

a

+

1; // Ошибка

struct S { }

 

 

 

 

 

const

S q

= new

S

{

}; // Ошибка

const

int

z =

z

+

1; // Ошибка

const

int

a2

= b2

+

1; // Ошибка

const

int

b2

= a2

+

1; // Ошибка

const

string

S1

=

"!!!"; // ОК

const

Type S2

=

typeof(int); // Ошибка

 

 

 

 

 

 

 

 

Пример: Samples\4.3\4_3_1_const.

4.3.2. Поля

Синтаксис:

266

<поле> :: [<атрибуты>] [<модификаторы>] <тип> <оператор объявления>

Как видно из таблицы 4.2, среди прочих модификаторов для полей применимы модификаторы static и readonly, причем их можно комбинировать. Чем будут отличаться поля, описанные с такими модификаторами и без них? И чем они отличаются от констант? Отличия приведены в табл. 4.3.

Табл. 4.3 – Отличия модификаторов полей класса

 

 

 

const

readonly

static

static

 

 

 

 

 

 

 

readonly

 

 

 

 

 

 

 

Инициализация

при

нужно

можно

можно

можно

можно

объявлении

 

 

 

 

 

 

 

 

 

 

 

 

 

Можно

инициализи-

+

+

ровать в

статическом

 

 

 

 

 

конструкторе

 

 

 

 

 

 

 

 

 

 

 

 

 

Можно

инициализи-

+

+

+

ровать в конструкторе

 

 

 

 

 

экземпляра

 

 

 

 

 

 

 

 

 

 

 

 

 

Можно

инициализи-

+

ровать в

статическом

 

 

 

 

 

методе

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Можно

инициализи-

+

+

ровать в методе эк-

 

 

 

 

 

земпляра

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Модификация

извне

+

+

класса

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Инициализация

зна-

+

+

+

+

чением, которое не-

 

 

 

 

 

возможно

вычислить

 

 

 

 

 

во время компиляции

 

 

 

 

 

 

 

 

 

 

 

 

 

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

Пример:

class MyClass

{

267

const int Field10; // Ошибка

public int Field11; // Предупреждение

public readonly int Field12; // Предупреждение public static int Field13; // Предупреждение

public static readonly int Field14; // Предупреждение

public const int Field20 = 0; // ОК public int Field21 = 1; // ОК

public readonly int Field22 = 2; // ОК public static int Field23 = 3; // ОК

public static readonly int Field24 = 4; // ОК

public const double Field30 = Math.Sqrt(2.0); // Ошибка public double Field31 = Math.Sqrt(2.0); // ОК

public readonly double Field32 = Math.Sqrt(2.0); // ОК public static double Field33 = Math.Sqrt(2.0); // ОК

public static readonly double Field34 = Math.Sqrt(2.0); // ОК

static MyClass()

{

Field20 = 0; // Ошибка Field21 = 1; // Ошибка Field22 = 2; // Ошибка Field23 = 3; // ОК Field24 = 4; // ОК

}

public MyClass()

{

Field20 = 0; // Ошибка Field21 = 1; // ОК Field22 = 2; // ОК Field23 = 3; // ОК Field24 = 4; // Ошибка

}

public static void Method2()

{

Field20 = 0; // Ошибка Field21 = 1; // Ошибка Field22 = 2; // Ошибка Field23 = 3; // ОК Field24 = 4; // Ошибка

}

public void Method1()

{

Field20 = 0; // Ошибка Field21 = 1; // ОК Field22 = 2; // Ошибка Field23 = 3; // ОК Field24 = 4; // Ошибка

}

}

static int Main()

{

MyClass cls = new MyClass(); MyClass.Field20 = 1; // Ошибка cls.Field21 = 1; // ОК

268

cls.Field22 = 2; // Ошибка MyClass.Field23 = 3; // ОК MyClass.Field24 = 4; // Ошибка return 0;

}

Пример: Samples\4.3\4_3_2_fields.

269

§ 4.4. Описание методов класса

Среди методов классов можно выделить:

методы экземпляров;

статические методы;

конструкторы экземпляров;

статические конструкторы;

деструкторы;

метод Main.

4.4.1. Синтаксис описания методов

Общий синтаксис описания методов следующий:

<метод> :: [<атрибуты>] [<модификаторы>] [static]

<тип возвращаемого значения> <имя метода> [<параметры типа>] ([<список формальных параметров>]) [<ограничения параметров типа>] <тело метода>

<имя метода> :: <идентификатор> <имя метода> :: <тип интерфейса>.<идентификатор>

<тело метода> :: <блок> <тело метода> :: ;

<список формальных параметров> :: <фиксированный параметр> [, ...]

<фиксированный параметр> :: [<атрибуты>] [<модификатор параметра>] <тип> <идентификатор>

<модификатор параметра> :: ref <модификатор параметра> :: out <модификатор параметра> :: this <модификатор параметра> :: params

Как уже было сказано выше, параметры типа будут рассмотрены в § 5.1. Типом возвращаемого значения может быть любой рассмотренный нами ранее тип данных, а также специальный тип void (псевдоним структуры System.Void), который означает, что метод ничего не возвращает.

Синтаксис вызова статического метода и метода экземпляра будет следующим:

<вызов статического метода> :: <имя класса>.<идентификатор> ([<список фактических параметров>])

<вызов метода экземпляра> :: <экземпляр класса>.<идентификатор> ([<список фактических параметров>])

<список фактических параметров> :: <фактический параметр> [, ...]

270