Структуры

Автор: admin | 20 Июнь 2008 – 21:30 -


Структуры

Рассмотрим теперь более подробно вопросы описания структур, их синтаксиса, семантики и тех особенностей, что отличают их от классов.

Синтаксис структур

Синтаксис объявления структуры аналогичен синтаксису объявления класса:

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

{тело_структуры}

Какие изменения произошли в синтаксисе в сравнении с синтаксисом класса, описанным в лекции 16? Их немного. Перечислим их:

  • ключевое слово class изменено на слово struct;
  • список родителей, который для классов, наряду с именами интерфейсов, мог включать имя родительского класса, заменен списком интерфейсов. Для структур не может быть задан родитель (класс или структура). Заметьте, структура может наследовать интерфейсы;
  • для структур неприменимы модификаторы abstract и sealed. Причиной является отсутствие механизма наследования.

Все, что может быть вложено в тело класса, может быть вложено и в тело структуры: поля, методы, конструкторы и прочее, включая классы и интерфейсы.

Аналогично классу, структура может иметь статические и не статические поля и методы, может иметь несколько конструкторов, в том числе статические и закрытые конструкторы. Для структур можно создавать собственные константы, используя поля с атрибутом readonly и статический конструктор. Структуры похожи на классы по своему описанию и ведут себя сходным образом, хотя и имеют существенные различия в семантике присваивания.

Перечислим ограничения, накладываемые на структуры.

  • Самое серьезное ограничение связано с ограничением наследования. У структуры не может быть наследников. У структуры не может быть задан родительский класс или родительская структура. Конечно, всякая структура, как и любой класс в C#, является наследником класса Object, наследуя все свойства и методы этого класса. Структура может быть наследником одного или нескольких интерфейсов, реализуя методы этих интерфейсов.
  • Второе серьезное ограничение связано с процессом создания объектов. Пусть T – структура, и дано объявление без инициализации – T x. Это объявление корректно, в результате будет создан объект без явного вызова операции new. Сущности x будет отведена память, и на этой памяти будет развернут объект. Но поля объекта не будут инициализированы и, следовательно, не будут доступны для использования в вычислениях. Об этих особенностях подробно говорилось при рассмотрении значимых типов. В этом отношении все, что верно для типа int, верно и для всех структур.
  • Если при объявлении класса его поля можно инициализировать, что найдет отражение при работе конструктора класса, то поля структуры не могут быть инициализированы.
  • Конструктор по умолчанию у структур имеется, при его вызове поля инициализируются значениями по умолчанию. Этот конструктор нельзя заменить, создав собственный конструктор без аргументов.
  • В конструкторе нельзя вызывать методы класса. Поля структуры должны быть проинициализированы до вызова методов.

Класс Rational или структура Rational

Вернемся к классу Rational, спроектированному в предыдущей лекции. Очевидно, что его вполне разумно представить в виде структуры. Наследование ему не нужно. Семантика присваивания развернутого типа больше подходит для рациональных чисел, чем ссылочная семантика, ведь рациональные числа – это еще один подкласс арифметического класса. В общем, класс Rational – прямой кандидат в структуры. Зададимся вопросом, насколько просто объявление класса превратить в объявление структуры? Достаточно ли заменить слово class словом struct? В данном случае одним словом не обойтись. Есть одно мешающее ограничение на структуры. В конструкторе класса Rational вызывается метод nod, а вызов методов в конструкторе запрещен. Нетрудно обойти это ограничение, изменив конструктор, то есть явно задав вычисление общего делителя в его теле. Приведу текст этого конструктора:

public struct Rational

{

public Rational(int a, int b)

{

if(b==0) {m=0; n=1;}

else

{

//приведение знака

if( b<0) {b=-b; a=-a;}

//приведение к несократимой дроби

int p = 1, m1=a, n1 =b;

m1=Math.Abs(m1); n1 =Math.Abs(n1);

if(n1>m1){p=m1; m1=n1; n1=p;}

do

{

p = m1%n1; m1=n1; n1=p;

}while (n1!=0);

p=m1;

m=a/p; n=b/p;

}

}//Конструктор

//поля и методы класса

}

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

public void TwoSemantics()

{

Rational r1 = new Rational(1,3), r2 = new Rational(3,5);

Rational r3, r4;

r3 = r1+r2; r4 = r3;

if(r3 >1) r3 = r1+r3 + Rational.One;

else r3 = r2+r3 – Rational.One;

r3.PrintRational(“r3″); r4.PrintRational(“r4″);

}

В этом примере используются константы, работает статический конструктор, закрытый конструктор, перегруженные операции сравнения, арифметические выражения над рациональными числами. В результате вычислений r3 получит значение 8/15, r4- 14/15. Заметьте, аналогичный пример для класса Rational даст те же результаты. Для класса Rational и структуры Rational нельзя обнаружить разницу между ссылочным и развернутым присваиванием. Это связано с особенностью класса Rational – он по построению относится к неизменяемым (immutable) классам, аналогично классу String. Операции этого класса не изменяют поля объекта, а каждый раз создают новый объект. В этом случае можно считать, что объекты класса обладают присваиванием развернутого типа.

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

Как уже говорилось, все значимые типы языка реализованы структурами. В библиотеке FCL имеются и другие встроенные структуры. Рассмотрим в качестве примера структуры Point, PointF, Size, SizeF и Rectangle, находящиеся в пространстве имен System.Drawing и активно используемые при работе с графическими объектами. Первые четыре структуры имеют два открытых поля X и Y (Height и Width), задающие для точек – структур Point и PointF – координаты, целочисленные или в форме с плавающей точкой. Для размеров – структур Size и SizeF – они задают высоту и ширину, целочисленными значениями или в форме с плавающей точкой. Структуры Point и Size позволяют задать прямоугольную область – структуру Rectangle. Конструктору прямоугольника можно передать в качестве аргументов две структуры – точку, задающую координаты левого верхнего угла прямоугольника, и размер – высоту и ширину прямоугольника.

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

public void TestPointAndSize()

{

Point pt1 = new Point(3,5), pt2 = new Point(7,10), pt3;

PointF pt4 = new PointF(4.55f,6.75f);

Size sz1 = new Size(10,20), sz2;

SizeF sz3 = new SizeF(10.3f, 20.7f);

pt3 = Point.Round(pt4);

sz2 = new Size(pt1);

Console.WriteLine (“pt1: ” + pt1);

Console.WriteLine (“sz2 =new Size(pt1): ” + sz2);

Console.WriteLine (“pt4: ” + pt4);

Console.WriteLine (“pt3 =Point.Round(pt4): ” + pt3);

pt1.Offset(5,7);

Console.WriteLine (“pt1.Offset(5,7): ” + pt1);

Console.WriteLine (“pt2: ” + pt2);

pt2 = pt2+ sz2;

Console.WriteLine (“pt2= pt2+ sz2: ” + pt2);

}//TestPointAndSize

Результаты его выполнения показаны на рис. 17.1


Рис. 17.1. Операции над точками и размерами

Отметим, что метод ToString, определенный для этих структур, выдает строку со значениями полей в приемлемой для восприятия форме.

Еще раз о двух семантиках присваивания

В заключение разговора о ссылочных и развернутых типах построим класс CPoint, являющийся полным аналогом структуры Point. Не буду приводить описание этого класса – надеюсь, оно достаточно понятно. Ограничусь примером, в котором аналогичные действия выполняются над объектами, принадлежащими структуре Point и классу CPoint:

public void TestTwoSemantics()

{

Console.WriteLine(“Структуры: присваивание

развернутого типа!”);

Point pt1 = new Point(3,5), pt2;

pt2 = pt1;

Console.WriteLine (“pt1: ” + pt1);

Console.WriteLine (“pt2=pt1: ” + pt2);

pt1.X +=10;

Console.WriteLine (“pt1.X =pt1.X +10: ” + pt1);

Console.WriteLine (“pt2: ” + pt2);

Console.WriteLine(“Классы: присваивание ссылочного типа!”);

CPoint cpt1 = new CPoint(3,5), cpt2;

cpt2 = cpt1;

Console.WriteLine (“cpt1: ” + cpt1);

Console.WriteLine (“cpt2=cpt1: ” + cpt2);

cpt1.X +=10;

Console.WriteLine (“cpt1.X =cpt1.X +10: ” + cpt1);

Console.WriteLine (“cpt2: ” + cpt2);

}

Результаты вычислений показаны на рис. 17.2.


Рис. 17.2. Две семантики присваивания

Действия над объектами Point и CPoint выполняются аналогичные а результаты получаются разные: в конце вычислений pt1 и pt2 различны, а cpt1 и cpt2 совпадают.


Tags: , , , , , , , , ,
Находится в Учебник | No Comments »

Ответить

Вы должны быть в системе, дабы комментировать.


C# — язык программирования, сочетающий объектно-ориентированные и аспектно-ориентированные концепции. Разработан в 1998—2001 годах группой инженеров под руководством Андерса Хейлсберга в компании Microsoft как основной язык разработки приложений для платформы Microsoft .NET. Компилятор с C# входит в стандартную установку самой .NET, поэтому программы на нём можно создавать и компилировать даже без инструментальных средств вроде Visual Studio. Конструкторы Smoby: кухня smoby. Кухонная мебель на заказ. C# относится к семье языков с C-подобным синтаксисом, из них его синтаксис наиболее близок к С++ и Java. Язык имеет строгую статическую типизацию, поддерживает полиморфизм, перегрузку операторов, указатели на функции-члены классов, атрибуты, события, свойства, исключения, комментарии в формате XML. Переняв многое от своих предшественников — языков С++, Java, Delphi, Модула и Smalltalk — С#, опираясь на практику их использования, исключает некоторые модели, зарекомендовавшие себя как проблематичные при разработке программных систем: так, C# не поддерживает множественное наследование классов (в отличие от C++).