Классы типов данных C#
|
Типы данных в CTS
|
Ключевое слово в C#
|
System.Byte
|
byte
|
System.SByte
|
sbyte
|
System.Int16
|
short
|
System.Int32
|
int
|
System.Int64
|
long
|
System.UInt16
|
ushort
|
System.UInt32
|
uint
|
System.UInt64
|
ulong
|
System.Single
|
float
|
System.Double
|
double
|
System.Object
|
object
|
System.Char
|
char
|
System.String
|
String
|
System.Boolean
|
bool
|
Преимущества
использования общих типов
I. Наиболее очевидное - повторное использование кода. Нет
необходимости создавать два идентичных класса, отличающихся только типами
параметров, достаточно создать один с параметризованными типами. При этом
использование параметризованных типов позволяет создавать единый программный
код для работы с различными типами данных. Например, единожды написанный
алгоритм может работать и с целыми числами и с числами с плавающей десятичной
точкой, при этом не производя на каждом шаге проверку/приведение типа. Так
Generics вытесняют классы объявленные с использованием типа object.. Повышение
производительности кода по сравнению с использование параметров типа object -
нет необходимости выполнять приведение, как уже сказано выше, на каждом шаге,
за счет чего получается выигрыш в производительности.. Проверка типов в момент
компиляции программы. Поскольку не используются параметры типа object, то
компилятор может выполнить проверку типа каждого параметра в момент компиляции,
поскольку типы для Generic классов жестко задаются в момент объявления
переменных классов этого типа.
Интерфейсы в C#
Синтаксис
интерфейса
Интерфейс является специальным видом классов. В нем задается набор
абстрактных методов, свойств и индексаторов, которые должны быть реализованы в
производных классах. Иными словами, интерфейс определяет поведение, которое
поддерживается реализующими этот интерфейс классами. Основная идея
использования интерфейса состоит в том, чтобы к объектам таких классов можно
было обращаться одинаковым образом.
Каждый класс может определять элементы интерфейса по-своему.
Так достигается полиморфизм: объекты разных классов по-разному реагируют
на вызовы одного и того же метода. Синтаксис интерфейса аналогичен синтаксису
класса:
[атрибуты][спецификаторы] interface <имя_интерфейса>
[:предки] <тело_интерфейса> [;]
Для интерфейса могут быть указаны спецификаторы, new, publiс, protected, internal и private. Спецификатор new применяется для вложенных
интерфейсов и имеет такой же смысл, как и соответствующий модификатор метода
класса. Остальные спецификаторы управляют видимостью интерфейса. По умолчанию
интерфейс доступен только из сборки, в которой он описан (internal). Интерфейс
может наследовать свойства нескольких интерфейсов, в этом случае предки
перечисляются через запятую. Тело интерфейса составляют абстрактные методы,
шаблоны свойств и индексаторов, а также события.
Интерфейс не может содержать константы, поля, операции, конструкторы,
деструкторы, типы и любые статические элементы. В интерфейсе методы неявно
являются открытыми (public-методами), при этом не разрешается явным образом
указывать спецификатор доступа.
В интерфейсе имеет смысл задавать заголовки тех методов и свойств,
которые будут по-разному реализованы различными классами разных иерархий.
Интерфейсы же чаще используются для задания общих свойств объектов различных
иерархий. Отличия интерфейса от абстрактного класса:
• элементы интерфейса по умолчанию имеют спецификатор доступа publiс и не
могут иметь спецификаторов, заданных явным образом;
• интерфейс не может содержать полей и обычных методов - все элементы
интерфейса должны быть абстрактными;
• класс может иметь в списке предков несколько интерфейсов, при этом он
должен определять все их методы.
Реализация
интерфейса
Чтобы реализовать интерфейс, нужно указать его имя после имени класса. В
списке предков класса сначала указывается его базовый класс, если он есть, а
затем через запятую - интерфейсы, которые реализует этот класс. Таким образом,
в С# поддерживается одиночное наследование для классов и множественное - для
интерфейсов. Это позволяет придать производному классу свойства нескольких
базовых интерфейсов, реализуя их по своему усмотрению.
Формат записи класса, который реализует интерфейс, таков:
class <имя_класса> : <имя__интерфейса>
{
// тело класса
}
Использование
интерфейсных ссылок
Можно объявить ссылочную переменную интерфейсного типа. Такая переменная
может ссылаться на любой объект, который реализует ее интерфейс. При вызове
метода для объекта посредством интерфейсной ссылки будет выполнена та версия
указанного метода, которая реализована этим объектом. Этот процесс аналогичен
использованию ссылки на базовый класс для доступа к объекту производного
класса.
Явная реализация интерфейса
При реализации члена интерфейса можно квалифицировать его имя с
использованием имени интерфейса. В этом случае говорят, что член интерфейса
реализуется явным образом, или имеет место его явная реализация.
Имя интерфейса явно указывается перед реализуемым элементом через точку.
Спецификаторы доступа при этом не указываются. К таким элементам можно
обращаться в программе только через объект типа интерфейса.
Закрытая реализация
Реализуя метод с использованием полностью квалифицированного имени, мы
обозначаем части закрытой реализации, которые недоступны вне класса, т.е. при
явном задании имени реализуемого интерфейса соответствующий метод не входит в
интерфейс класса. Это позволяет упростить код программы в том случае, если
какие-то элементы интерфейса не требуются конечному пользователю класса.
Делегаты в C#
Делегат - это вид класса, предназначенный для хранения ссылок на методы.
Делегат, как и любой другой класс, можно передать в качестве параметра, а затем
вызвать инкапсулированный в нем метод. Делегаты используются для поддержки
событий, а также как самостоятельная конструкция языка. Рассмотрим сначала
второй случай.
Описание
делегатов
Описание делегата задает сигнатуру методов, которые могут быть вызваны с
его помощью:
[ атрибуты ] [ спецификаторы ] delegate <тип> <имя дел.> ( [
параметры ] )
Спецификаторы делегата имеют тот же смысл, что и для класса, причем
допускаются только спецификаторы new, public, protected, internal и private. Тип описывает возвращаемое значение методов,
вызываемых с помощью делегата, а необязательными параметрами делегата являются
параметры этих методов. Делегат может хранить ссылки на несколько методов и
вызывать их поочередно, естественно, что сигнатуры всех методов должны
совпадать.
Пример описания делегата: public delegate void Dd ( int i );
Здесь описан тип делегата, который может хранить ссылки на методы,
возвращающие void и принимающие один параметр целого типа.
Делегат, как и всякий класс, представляет собой тип данных. Его базовым
классом является класс System.Delegate. Наследовать от делегата нельзя.
Объявление делегата можно размещать непосредственно в пространстве имен
или внутри класса.
Использование
делегатов
Для того чтобы воспользоваться делегатом, необходимо создать его
экземпляр и задать имена методов, на которые он будет ссылаться. При вызове
экземпляра делегата вызываются все заданные в нем методы.
Делегаты применяются в основном для следующих целей:
· получения возможности определять вызываемый метод не при
компиляции, а динамически во время выполнения программы;
· обеспечения связи между объектами по типу "источник -
наблюдатель";
· создания универсальных методов, в которые можно передавать
другие методы;
· поддержки механизма обратных вызовов.
Использование делегата имеет тот же синтаксис, что и вызов метода. Если
делегат хранит ссылки на несколько методов, они вызываются последовательно в
том порядке, в котором были добавлены в делегат.
Добавление метода в список выполняется либо с помощью метода Combine,
унаследованного от класса System.Delegate, либо, что удобнее, с помощью
перегруженной операции сложения.
При вызове последовательности методов с помощью делегата необходимо
учитывать следующее:
· сигнатура методов должна в точности соответствовать делегату;
· методы могут быть как статическими, так и обычными методами
класса;
· каждому методу в списке передается один и тот же набор
параметров;
· если параметр передается по ссылке, изменения параметра в
одном методе отразятся на его значении при вызове следующего метода;
· если параметр передается с ключевым словом out или метод
возвращает значение. Результатом выполнения делегата является значение,
сформированное последним из методов списка (в связи с этим рекомендуется
формировать списки только из делегатов, имеющих возвращаемое значение типа voi
d);
· если в процессе работы метода возникло исключение, не
обработанное в том же методе, последующие методы в списке не выполняются, а
происходит поиск обработчиков в объемлющих делегат блоках;
· попытка вызвать делегат, в списке которого нет ни одного
метода, вызывает генерацию исключения System. Null
Ref erenceExcepti on.
Массивы
Массив относится к ссылочным типам данных, то есть располагается в
динамической области памяти, поэтому создание массива начинается с выделения
памяти под его элементы. Элементами массива могут быть величины как значимых,
так и ссылочных типов (в том числе массивы). Массив значимых типов хранит
значения, массив ссылочных типов - ссылки на элементы. Всем элементам при создании
массива присваиваются значения по умолчанию: нули для значимых типов и null -
для ссылочных.
Все массивы в С# имеют общий базовый класс Array, определенный в
пространстве имен System. В нем есть несколько полезных методов, упрощающих
работу с массивами, например методы получения размерности, сортировки и поиска.
Массивы, являющиеся полями класса, могут иметь те же спецификаторы, что и
поля, представляющие собой простые переменные.
Одномерные массивы используются в программах чаще всего. Варианты описания
массива:
тип[] имя;
тип[] имя = new тип [ размерность ];
тип[] имя = { список инициализаторов };
тип[] имя = new тип [ размерность ] { список инициализаторов };
Так же используются не редко многомерные (прямоугольные) массивы.
Варианты их описания могут быть следующими:
тип[ , ] имя;
тип[ , ] имя = new тип [ разм1, разм2];
тип[ , ] имя = { список инициализаторов };
тип[ , ] имя = new тип [ разм1, разм2] { список инициализаторов };
Использование
делегатов вместо интерфейсов
И делегаты, и интерфейсы позволяют конструктору классов отделять
объявление типов от реализации. Определенный интерфейс может быть унаследован и
реализован любым классом или структурой. Делегат может быть создан для метода в
любом классе, если метод соответствует сигнатуре метода для делегата. Ссылка на
интерфейс или делегат могут быть использованы объектом, не имеющим данных о
классе, реализующем интерфейс или метод делегата. Учитывая эти сходные
признаки, когда в конструкторе классов следует использовать делегат, а когда
следует использовать интерфейс?
Делегат следует использовать в следующих ситуациях:
· Используется шаблон разработки событий.
· Желательно инкапсулировать статический метод.
· Вызывающему не требуется доступ к другим свойствам, методам
или интерфейсам объекта для реализации метода.
· Желательно простое построение.
· Классу может потребоваться несколько реализаций метода.
Интерфейс следует использовать в следующих ситуациях:
· Существует группа связанных методов, которые могут быть
вызваны.
· Классу потребуется только одна реализация метода.
· Класс, использующий интерфейс, будет передавать этот
интерфейс другим типам классов и интерфейсов.
· Реализуемый метод связан с типом или идентификатором класса:
например, методы сравнения.
Хорошим примером использования интерфейса с одним методом вместо делегата
является IComparable или более общая версия IComparable<(Of
<(T>)>). В IComparable объявляется метод CompareTo, возвращающий целое
число, указывающее отношение (меньше, равно или больше) между двумя объектами
одинакового типа. Можно использовать IComparable в качестве основы для
алгоритма сортировки. В основе алгоритма сортировки можно использовать делегат
метода сравнения, но такой подход не является оптимальным. Возможность
сравнения относится к классу, а алгоритм сравнения не изменяется при
выполнении, поэтому лучше использовать интерфейс с одним методом.
Практика
Для более полного представления того, что я пыталась описать выше,
предлагаю для рассмотрения два примера, где использовалось описание и
интерфейсов и делегатов с помощью общих типов.
Задачи составлены аналогично друг другу, отличием является лишь условие
поиска в массиве.
А именно:
. Поиск заданного элемента в массиве.
. Поиск максимального элемента в массиве.
Программа 1
System;
//интерфейсinterface IElement
{
void newE();summ();
}
//класс реализующий интерфейсclass MyElement : IElement
{string name;int e1;int e2;
//метод для заполнения элементов класса
public void newE()
}
//подсчет суммы элементовint summ()
{(e1 + e2);
}
}
//описание делегатаint Delegate1(IElement[] arr, int param);
public class Test
{//метод для поиска элемента с заданной суммой в массиве (если его нет -
возвращает -1)
static int Find (IElement[] arr, int param)
{for (int i = 1; i < 5; i ++)
{if (arr[i].summ() == param)
{return i;} }-1;}static void Main()
{Delegate1 d1 = new Delegate1(Find);[] array1 = new
IElement[5];[0] = new MyElement();[0].newE();[1] = new MyElement();[1].newE();[2]
= new MyElement();[2].newE();[3] = new MyElement();[3].newE();[4] = new
MyElement();[4].newE();c = d1(array1,16);
Console.WriteLine("Номер элемента с заданной суммой:");
Console.WriteLine(c);}
}
Программа 2
using System;interface IElement
{ void newE();summ(); }class MyElement : IElement
{ public string name;int e1;int e2;void newE()
{ name = Console.ReadLine();=
Convert.ToInt32(Console.ReadLine());= Convert.ToInt32(Console.ReadLine());
}int summ()
{(e1 + e2);
}
}int Delegate1(IElement[] arr);class Test
{static int MaxElem (IElement[] arr)
{int n = 0;max = arr[0].summ();(int i = 1; i < 5; i ++)
{if (arr[i].summ() > max)
{max = arr[i].summ();= i;
}
}n;
}static void Main()
{d1 = new Delegate1(MaxElem);[] array1 = new IElement[5];[0]
= new MyElement();[0].newE();[1] = new MyElement();[1].newE();[2] = new
MyElement();[2].newE();[3] = new MyElement();[3].newE();[4] = new
MyElement();[4].newE();c = d1(array1);
Console.WriteLine("Номер элемента с максимальной
суммой:");.WriteLine(c);
}
}
Заключение
В ходе изучения данной темы моей целью было рассмотреть, как используются
общие типы при описании интерфейсов и делегатов, и рассмотреть это на примерах
задач поиска в массивах.
При детальном изучении данной темы я выяснила, что использование в
программе и интерфейсов и делегатов значительно упрощает написание программы и
решение поставленной задачи, так как сама программа может быть описана намного
короче и стать более просто для понимания.
Литература
1) Л.Е. Потапова "Объектно-ориентированное программирование
на языке С#"
) Т.Е. Павловская "Программирование на языке
С#"
3) Tdoc.ru "Новинки
языка C#.NET"
) Википедия
) Э. Стиллмен, Дж. Грин "Изучаем С#"