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

ооп теория

.pdf
Скачиваний:
19
Добавлен:
14.02.2015
Размер:
3.58 Mб
Скачать

Рис. 6.1. Включение свойства /unsafe

///<summary>

///определение размеров и типов

///</summary>

unsafe public static void SizeMethod()

{

Console.WriteLine("Размер типа Boolean = " + sizeof(bool)); Console.WriteLine("Размер типа double = " + sizeof(double)); Console.WriteLine("Размер типа char = " + sizeof(System.Char)); int b1=1;

Type t = b1.GetType();

Console.WriteLine("Тип переменной b1: {0}", t); //Console.WriteLine("Размер переменной b1: {0}", sizeof(t));

}//SizeMethod

В этом примере операция применяется к трем встроенным типам - bool, double, char. Консольный вывод дает в качестве результата значения: 1, 8 и 2.

Обращаю внимание на то, что аргументом операции может быть только имя типа. Попытка применить эту операцию к переменной t типа Type, имеющей значение System.Int32, приводит к ошибке компиляции.

Операция typeof, примененная к своему аргументу, возвращает его тип.

И здесь в роли аргумента может выступать имя класса, как встроенного, так и созданного пользователем. Возвращаемый операцией результат имеет тип

Type. К экземпляру класса применять операцию нельзя, но зато для экземпляра можно вызвать метод GetType, наследуемый всеми классами, и

101

получить тот же результат, что дает typeof с именем данного класса. Такой альтернативный способ получения типа по экземпляру класса int показан в приведенном выше программном фрагменте. А сейчас я приведу фрагмент,

где используется вызов операции typeof:

t = typeof(Class1);

Console.WriteLine("Тип класса Class1: {0}", t); t = typeof(Testing);

Console.WriteLine("Тип класса Testing: {0}", t);

КАК ПОЛУЧИТЬ ПОДРОБНУЮ ИНФОРМАЦИЮ О КЛАССЕ?

Пожалуй, следует рассказать не только о том, как можно получить переменную типа Type, а и о том, что можно с этой переменной делать.

Этот и последующий раздел прерывают последовательное рассмотрение темы операций языка C#. Полагаю, понимание того, с какой целью выполняются те или иные операции, не менее важно, чем знание самой операции, и я не стал откладывать изложение этого материала на последующие лекции.

Можно ли, зная тип (класс), получить подробную информацию обо всех методах и полях класса? Ясно, что такая информация может быть весьма полезной, если класс поставлен сторонней фирмой. Оказывается, это сделать нетрудно. Вся необходимая информация содержится в метаданных,

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

использование классов пространства имен Reflection, добавив в начало проекта

предложение

using System.Reflection;

102

В класс Testing я добавил существенно расширенный вариант метода WhoIsWho, который уже появлялся в наших примерах. Вот текст новой версии этой процедуры:

///<summary>

///Подробная информация о классе объекта, его значении,

///методах класса, всех членов класса

///</summary>

///<param name="name">имя объекта</param>

///<param name="any">объект любого типа</param>

public void WhoIsWho(string name,object any)

{

Type t = any.GetType();

Console.WriteLine("Тип {0}: {1} , значение: {2}", name, any.GetType(), any.ToString());

Console.WriteLine("Методы класса:"); MethodInfo[] ClassMethods = t.GetMethods(); foreach (MethodInfo curMethod in ClassMethods)

{

Console.WriteLine(curMethod);

}

Console.WriteLine("Все члены класса:"); MemberInfo[] ClassMembers = t.GetMembers(); foreach (MemberInfo curMember in ClassMembers)

{

Console.WriteLine(curMember.ToString());

}

}//WhoIsWho

Коротко прокомментирую эту процедуру. Вначале создается переменная t типа Type. Значением этой переменной будет тип аргумента,

переданного в процедуру в качестве значения параметра any. Напомню, any

имеет базовый тип object и потому метод может быть вызван с аргументом,

роль которого может играть выражение любого типа. Далее вызываются методы переменной t - GetMethods() и GetMembers(). Эти методы соответственно возвращают в качестве значений массивы элементов классов

MethodInfo и MemberInfo. Эти классы содержатся в пространстве имен

Reflection, они хранят информацию в первом случае о методах класса, во втором - о полях и методах класса, заданного переменной t. В пространстве имен Reflection имеются и другие классы, чьи методы и свойства полезны для получения дополнительной информации об исследуемом классе. Но я не буду сейчас столь подробно развивать эту тему.

103

В процедуре Main дважды вызывается процедура WhoIsWho. В первом вызове ее аргументом является выражение типа double, во втором - сам объект ts, вызывающий метод:

ts.WhoIsWho("2+2", 2+2); ts.WhoIsWho("ts", ts);

И класс double, и созданный в этом проекте класс Testing имеют довольно много методов. Имеют они и свойства. Процедура WhoIsWho

выдаст подробную информацию обо всех элементах этих классов.

Результаты консольного вывода, полученного при двух вызовах этой процедуры, показаны на рис. 6.2.

Рис. 6.2. Информация о классах int и Testing, полученная в процедуре WhoIsWho

Рассмотрим выводимую информацию о классах. Для созданного в проекте класса Testing отображается информация о полях и методах как собственных, так и наследуемых от общего родителя - класса Object.

104

Заметьте, отображается информация только об открытых полях и методах класса, а поскольку поля нашего класса закрыты, то и информации о них нет.

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

СТАТИЧЕСКИЕ ПОЛЯ И МЕТОДЫ АРИФМЕТИЧЕСКИХ КЛАССОВ

Все арифметические классы, в том числе класс Int, обладают двумя полезными полями (свойствами) - MinValue и MaxValue. Эти поля возвращают минимальное и максимальное значение, которое могут иметь экземпляры класса. Поля являются статическими и потому недоступны для экземпляров класса и могут быть вызваны только при указании имени класса.

Разумно привести пример вызова этих полей для класса Int и, например, для класса Double:

//Min и Max значения типов

Console.WriteLine("Class int");

Console.WriteLine("Мин. значение int = " + int.MinValue); Console.WriteLine("Макс. значение int = " + int.MaxValue); Console.WriteLine("Class double");

Console.WriteLine("Мин. значение double = " + double.MinValue); Console.WriteLine("Макс. значение double = " + double.MaxValue);

Все арифметические классы, в том числе класс Int, обладают перегруженным статическим методом Parse, у которого первым обязательным параметром является строка, задающая значение соответствующего арифметического типа в привычной для данного региона

(локализованной) форме. Форматом строки и стилем ее представления можно управлять с помощью других параметров метода Parse. Вот пример вызова

этого метода для классов Int и Double:

105

///<summary>

///Преобразования типа с использованием метода Parse

///</summary>

public void Parsing()

{

//method Parse Console.WriteLine("Введите целое"); string strdata = Console.ReadLine(); int intdata = int.Parse(strdata);

Console.WriteLine("Введите число с дробной частью и порядком"); strdata = Console.ReadLine();

double doubdata = double.Parse(strdata); Console.WriteLine("intdata = {0}; doubdata = {1}",

intdata, doubdata);

}

//Parsing

Как видите, метод Parse с успехом заменяет соответствующий метод класса

Convert.

На рис. 6.3 можно увидеть консольный вывод, полученный в результате работы процедуры Parsing.

Рис. 6.3. Результаты работы процедуры Parsing

ОПЕРАЦИЯ NEW

Пора вернуться к основной теме - операциям, допустимым в языке C#.

Последней из еще не рассмотренных операций высшего уровня приоритета является операция new. Ключевое слово new используется в двух контекстах

- как модификатор и как операция в инициализирующих выражениях объявителя. Во втором случае результатом выполнения операции new

является создание нового объекта и вызов соответствующего конструктора.

Примеров подобного использования операции new было приведено достаточно много, в том числе и в этой лекции.

АРИФМЕТИЧЕСКИЕ ОПЕРАЦИИ

106

В языке C# имеются обычные для всех языков арифметические операции - "+, -, *, /, %". Все они перегружены. Операции "+" и "-" могут быть унарными и бинарными. Операция деления "/" над целыми типами осуществляет деление нацело, для типов с плавающей и фиксированной точкой - обычное деление. Операция "%" определена над всеми арифметическими типами и возвращает остаток от деления нацело. Тип результата зависит от типов операндов. Приведу пример вычислений с различными арифметическими типами:

///<summary>

///Арифметические операции

///</summary>

public void Ariphmetica()

{

int n = 7,m =3, p,q; p= n/m; q= p*m + n%m;

if (q==n) Console.WriteLine("q=n"); else Console.WriteLine("q!=n"); double x=7, y =3, u,v,w;

u = x/y; v= u*y; w= x%y;

if (v==x) Console.WriteLine("v=x"); else Console.WriteLine("v!=x"); decimal d1=7, d2 =3, d3,d4,d5;

d3 = d1/d2; d4= d3*d2; d5= d1%d2;

if (d4==d1) Console.WriteLine("d4=d1"); else Console.WriteLine("d4!=d1");

}//Ariphmetica

При проведении вычислений в первых двух случаях проверяемое условие оказалось истинным, в третьем - ложным. Для целых типов можно исходить из того, что равенство n = n/m*m + n%m истинно. Для типов с плавающей точкой выполнение точного равенства x = x/y*y следует считать скорее случайным, а не закономерным событием. Законно невыполнение этого равенства, как это имеет место при вычислениях с фиксированной точкой.

ОПЕРАЦИИ ОТНОШЕНИЯ

107

Операции отношения можно просто перечислить - в объяснениях они не нуждаются. Всего операций 6 (==, !=, <, >, <=, >= ). Для тех, кто не привык работать с языком C++, стоит обратить внимание на запись операций

"равно" и "не равно".

ОПЕРАЦИИ ПРОВЕРКИ ТИПОВ

Операции проверки типов is и as будут рассмотрены в последующих лекциях. (см. лекцию 19).

ОПЕРАЦИИ СДВИГА

Операции сдвига вправо ">>" и сдвига влево "<<" в обычных вычислениях применяются редко. Они особенно полезны, если данные рассматриваются как строка битов. Результатом операции является сдвиг строки битов влево или вправо на K разрядов. В применении к обычным целым положительным числам сдвиг вправо равносилен делению нацело на

2K, а сдвиг влево - умножению на 2K. Для отрицательных чисел сдвиг влево и деление дают разные результаты, отличающиеся на единицу. В языке C#

операции сдвига определены только для некоторых целочисленных типов - int, uint, long, ulong. Величина сдвига должна иметь тип int. Вот пример применения этих операций:

///<summary>

///операции сдвига

///</summary> public void Shift()

{

int n = 17,m =3, p,q; p= n>>2; q = m<<2;

Console.WriteLine("n= " + n + "; m= " + m + "; p=n>>2 = "+p

+"; q=m<<2 " + q);

long x=-75, y =-333, u,v,w; u = x>>2; v = y<<2; w = x/4;

Console.WriteLine("x= " + x + "; y= " + y + "; u=x>>2 = "+u + "; v=y<<2 " + v + "; w = x/4 = " + w);

}//Shift

ЛОГИЧЕСКИЕ ОПЕРАЦИИ

108

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

нулевое - как ложь. В языке C# неявных преобразований к логическому типу нет даже для целых арифметических типов. Поэтому вполне корректная в языке C++ запись:

int k1 = 7;

if (k1) Console.WriteLine("ok!");

незаконна в программах на C#. На этапе трансляции возникнет ошибка, поскольку вычисляемое условие имеет тип int, а неявное преобразование этого типа к типу bool

отсутствует.

В языке C# более строгие правила действуют и для логических операций. Так,

запись

if(k1 && (x>y)),

корректная в языке C++, приводит к ошибке в программах на C#,

поскольку операция && определена только для операндов типа bool, а в данном выражении один из операндов имеет тип int. В языке C# в данных ситуациях следует использовать записи:

if(k1>0)

if((k1>0) && (x>y))

После этого важного предупреждения перейду к более систематическому изложению некоторых особенностей выполнения логических операций. Так же, как и в языке C++, логические операции делятся на две категории: одни выполняются над логическими значениями операндов, другие осуществляют выполнение логической операции над битами операндов. По этой причине в C# существуют две унарные операции отрицания - логическое отрицание, заданное операцией "!", и побитовое

109

отрицание, заданное операцией "~". Первая из них определена над операндом типа bool, вторая - над операндом целочисленного типа, начиная с типа int и

выше (int, uint, long, ulong). Результатом операции во втором случае является операнд, в котором каждый бит заменен его дополнением. Приведу пример:

///<summary>

///Логические выражения

///</summary>

public void Logic()

{

//операции отрицания ~,! bool b1,b2;

b1 = 2*2==4; b2 =!b1; //b2= ~b1;

uint j1 =7, j2; j2= ~j1;

//j2 = !j1;

int j4 = 7, j5; j5 = ~j4;

Console.WriteLine("uint j2 = " + j2 +" int j5 = " + j5); }//Logic

В этом фрагменте закомментированы операторы, приводящие к ошибкам. В первом случае была сделана попытка применения операции побитового отрицания к выражению типа bool, во втором - логическое отрицание применялось к целочисленным данным. И то, и другое в C#

незаконно. Обратите внимание на разную интерпретацию побитового отрицания для беззнаковых и знаковых целочисленных типов. Для переменных j5 и j2 строка битов, задающая значение - одна и та же, но интерпретируется по-разному. Соответствующий вывод таков:

uint j2 = 4294967288 int j5 = -8

Бинарные логические операции "&& - условное И" и "|| - условное

ИЛИ" определены только над данными типа bool. Операции называются условными или краткими, поскольку, будет ли вычисляться второй операнд,

зависит от уже вычисленного значения первого операнда. В операции "&&",

если первый операнд равен значению false, то второй операнд не вычисляется и результат операции равен false. Аналогично, в операции "||",

110