
Статические классы
Часто бывает использовать классы, содержащие только статические члены, для которых невозможно создавать объекты. Примером такого класса является класс Console. Проще всего в этой ситуации не делать все конструкторы класса приватными, а использовать статические классы. Статический класс может содержать только статические члены и не может содержать конструкторы экземпляров.
13
New - Новое описание поля, скрывающее унаследованный элемент класса
Public - Доступ не ограничен
Protected - Используется для вложенных классов. Доступ только из элементов данного и производных классов
Internal - Доступ только из данной программы (сборки)
Protected internal - Доступ только из данного и производных классов или из данной программы (сборки)
Private - Используется для вложенных классов. Доступ только из элементов класса, внутри которого описан данный класс.
Abstract - Одно поле для всех экземпляров класса
Sealed - Поле доступно только для чтения
Static - Поле может изменяться другим процессом или системой
Readonly - только для чтения (Установить значение такого поля можно либо при его описании, либо в конструкторе)
По умолчанию элементы класса считаются закрытыми (private). Для полей класса этот вид доступа является предпочтительным. Все методы класса имеют непосредственный доступ к его закрытым полям.
Поля, описанные со спецификатором static, а также константы существуют в единственном экземпляре для всех объектов класса, поэтому к ним обращаются не через имя экземпляра, а через имя класса. Если класс содержит только статические элементы, экземпляр класса создавать не требуется.
Обращение к полю класса выполняется с помощью операции доступа (точка). Справа от точки задается имя поля, слева – имя экземпляра для обычных полей или имя класса для статических. В листинге ниже приведен пример простого класса Demo
using System;
namespace ConsoleApplication1
{
class Demo
{
public int a = 1; // поле данных
public const double c = 1.66; // константа
public static string s = "Demo"; // статическое поле класса
double y; // закрытое поле данных
}
14
Абстрактный класс в объектно-ориентированном программировании — базовый класс, который не предполагает создания экземпляров. Абстрактные классы реализуют на практике один из принципов ООП - полиморфизм. Абстрактный класс может содержать (и не содержать[1]) абстрактные методы и свойства. Абстрактный метод не реализуется для класса, в котором описан, однако должен быть реализован для его неабстрактных потомков. Абстрактные классы представляют собой наиболее общие абстракции, то есть имеющие наибольшийобъем и наименьшее содержание.
// Класс транспортное средство abstract class Vehicle { private int price; // стоимость тр. Средства private int maxspeed; // максимальная скорость private int year; // год выпуска private int curspeed; // текущая скорость // исключения для классов наследников protected Exception OutOfMaxBorder = new Exception("Исключение, превышена максимальна граница"); protected Exception NonBellowZero = new Exception("Исключение, введенное значение не может быть отрицательным!"); #region Properties // свойство Стоимость public int Price { get { return price; } set { if (value > 0) price = value; } } // свойство Максимальная скорость public int MaxSpeed { get { return maxspeed; } set { if (value > 0) maxspeed = value; } } #endregion }
Или этот (на выбор)
abstract class BaseClass // Abstract class
{
protected int _x = 100;
protected int _y = 150;
public abstract void AbstractMethod(); // Abstract method
public abstract int X { get; }
public abstract int Y { get; }
}
DerivedClass : BaseClass
{
public override void AbstractMethod()
{
_x++;
_y++;
}
public override int X // overriding property
{
get
{
return _x + 10;
}
}
public override int Y // overriding property
{
get
{
return _y + 10;
}
}
static void Main()
{
DerivedClass o = new DerivedClass();
o.AbstractMethod();
Console.WriteLine("x = {0}, y = {1}", o.X, o.Y);
}
15 Механизм наследования позволяет расширить или создавать конкретные классы от одного более общего класса. При порождении от базового класса важным становится вопрос о доступности этих членов. Приватные члены базового класса не доступны из дочернего класса, а общедоступные – доступны. хотелось бы иметь такие члены класса, которые доступны из базового, дочернего классов, но не из внешнего класса. Такой уровень доступности называется protected. Помимо уровня защиты членов класса в производном классе можно определить способ наследования. Члены базового класса могут быть виртуальными, это значит что в производном классе может содержаться альтернативная реализация virtual члена. Эта альтернативная реализация не отменяет исходной реализации, но делает ее недоступной извне. При отсутствии
альтернативной
реализации любой внешний код обращается
к коду этого члена в базовом классе.
16
Перегрузка методов
Часто бывает удобно, чтобы методы, реализующие один и тот же алгоритм для различных типов данных, имели одно и то же имя. Использование нескольких методов с одним и тем же именем, но различными типами параметров называется перегрузкой методов. Компилятор определяет, какой именно метод требуется вызвать, по типу фактических параметров. Этот процесс называется разрешением (resolution) перегрузки. Перегруженные методы имеют одно имя, но должны различаться параметрами, точнее их типами и способами передачи (out или ref). Например, методы, заголовки которых приведены ниже, имеют различные сигнатуры и считаются перегруженными:
int max( int a, int b )
int max( int a, ref int b )
Перегрузка широко используется в классах библиотеки .NET. Например, в стандартном классе Console метод WriteLine перегружен 19 раз для вывода величин разных типов.
Необязательные аргументы
В определении метода, конструктора, индексатора или делегата можно указать, что параметры являются обязательными или необязательными.При каждом вызове необходимо указывать аргументы для всех обязательных параметров, но можно опустить аргументы для необязательных параметров.
В рамках определения каждого необязательного параметра задается его значение по умолчанию.Если для параметра не передается аргумент, используется значение по умолчанию.Значения по умолчанию должны быть константами.
Необязательные параметры определяются в конце списка параметров после всех обязательных параметров.Если вызывающий объект задает аргумент для какого-либо из последующих необязательных параметров, он должен задать аргументы для всех предшествующих необязательных параметров.Разделенные запятыми пустые позиции в списке аргументов не поддерживаются.Например, в следующем коде метод экземпляра ExampleMethod определен одним или двумя необязательными параметра
C#
public void ExampleMethod(int required, string optionalstr = "default string",
int optionalint = 10)
Следующий вызов ExampleMethod вызывает ошибку компилятора, поскольку аргумент предоставлен для третьего параметра, а не для второго.
//anExample.ExampleMethod(3, ,4);
Но если имя третьего параметра известно, задачу можно выполнить с использованием именованного аргумента.
anExample.ExampleMethod(3, optionalint: 4);
IntelliSense использует квадратные скобки для указания необязательных параметров.
17
Виртуальным называется такой метод, который объявляется как virtual в базовом классе. Виртуальный метод отличается тем, что он может быть переопределен в одном или нескольких производных классах. Следовательно, у каждого производного класса может быть свой вариант виртуального метода. При их вызове по ссылке на базовый класс средствами языка С# определяется именно тот вариант виртуального метода, который следует вызывать, исходя из типа объекта, к которому происходит обращение по ссылке, причем это делается во время выполнения. Поэтому при ссылке на разные типы объектов выполняются разные варианты виртуального метода. Иными словами, вариант выполняемого виртуального метода выбирается по типу объекта, а не по типу ссылки на этот объект.
Метод объявляется как виртуальный в базовом классе с помощью ключевого слова virtual, указываемого перед его именем. Когда же виртуальный метод переопределяется в производном классе, то для этого используется модификатор override. А сам процесс повторного определения виртуального метода в производном классе называется переопределением метода. При переопределении имя, возвращаемый тип и сигнатура переопределяющего метода должны быть точно такими же, как и у того виртуального метода, который переопределяется. Кроме того, виртуальный метод не может быть объявлен как static или abstract.
Если при наличии многоуровневой иерархии виртуальный метод не переопределяется в производном классе, то выполняется ближайший его вариант, обнаруживаемый вверх по иерархии.
Пример:
// Создаем виртуальный метод
public virtual string FontInfo(Font obj)
{
string s = "Информация о шрифте: \n------------------\n\n" +
"Тип шрифта: " + typeFont +
"\nРазмер шрифта: " + fontSize + "\n";
return s;
}
}
// Производный класс 1 уровня
class ColorFont : Font
{
byte Color;
public ColorFont(byte Color, string TypeFont, short FontSize)
: base(TypeFont, FontSize)
{
this.Color = Color;
}
// Переопределение для виртуального метода
public override string FontInfo(Font obj)
{
// Используется ссылка на метод определенный в базовом классе Font
return base.FontInfo(obj) + "Цвет шрифта: " + Color + "\n";
}
// Создадим виртуальное свойство
public virtual byte color
{
set
{
Color = value;
}
get
{
return Color;
}
}
}
18
Интерфейс представляет собой полностью абстрактный класс, все методы которого абстрактны.
От абстрактного класса интерфейс отличается некоторыми деталями в синтаксисе и поведении:
● синтаксическое отличие состоит в том, что методы интерфейса объявляются без указания модификатора доступа
● отличие в поведении заключается в более жестких требованиях к потомкам.
Интерфейсы позволяют частично справиться с таким существенным недостатком языка C#, как отсутствие множественного наследования классов. Обеспечить возможность классу иметь несколько родителей - один полноценный класс, а остальные в виде интерфейсов, - основное назначение интерфейсов. Интерфейс позволяет описывать некоторые желательные свойства, которыми могут обладать объекты разных классов.
Опишем некоторый интерфейс, задающий дополнительные свойства объектов класса:
public interface IProps
{
void Prop1(string s);
void Prop2 (string name, int val);
}
У этого интерфейса два метода, которые и должны будут реализовать все классы -наследники интерфейса.
Заметим, что у методов нет модификаторов доступа. Класс, наследующий интерфейс и реализующий его методы, может реализовать их явно, объявляя соответствующие методы класса открытыми.
Вот пример:
public class Clain:IProps
{
public Clain() {}
public void Prop1(string s)
{
Console.WriteLine(s);
}
public void Prop2(string name, int val)
{
Console.WriteLine("name = {0}, val ={1}", name, val);
}
}//Clain - Класс реализует наследников.
19