Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ооп-экз.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
149.7 Кб
Скачать

Void iProps.Prop3() { Console.WriteLine("Свойство 3 интерфейса 1"); }

Void iPropsOne.Prop3() { Console.WriteLine("Свойство 3 интерфейса 2"); }

public void Prop3FromInterface1() { ((IProps)this).Prop3(); }

public void Prop3FromInterface2() { ((IPropsOne)this).Prop3(); }}

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

Наследование от общего предка

Проблема наследования от общего предка характерна, в первую очередь, для множественного наследования классов. Если класс C является наследником классов A и B, а те, в свой черед, являются наследниками класса Parent, то класс наследует свойства и методы своего предка Parent дважды, один раз получая их от класса A, другой от — B. Это явление называется еще дублирующим наследованием. Для классов ситуация осложняется тем, что классы A и B могли по-разному переопределить методы родителя и для потомков предстоит сложный выбор реализации.

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

21. Структуры (struct) и перечисления (enum). Отличия структур от классов.

Как вы уже знаете, классы — это ссылочные типы. Это означает, что к объектам классов доступ осуществляется через ссылку. Этим они отличаются от типов значений, к которым в С# реализован прямой доступ. Но иногда желательно получать прямой доступ и к объектам, как в случае нессылочных типов. Одна из причин для этого — эффективность. Ведь очевидно, что доступ к объектам классов через ссылки увеличивает расходы системных ресурсов, в том числе и памяти. Даже для очень маленьких объектов требуются существенные объемы памяти. Для компенсации упомянутых расходов времени и пространства в С# предусмотрены структуры. Структура подобна классу, но она относится к типу значений, а не к ссылочным типам. Структуры объявляются с использованием ключевого слова struct и синтаксически подобны классам. Формат записи структуры таков:

struct имя : интерфейсы { // объявления членов

Элемент имя означает имя структуры.

Структуры не могут наследовать другие структуры или классы. Структуры не могут использоваться в качестве базовых для других структур или классов. (Однако, подобно другим С#-типам, структуры наследуют класс object). Структура может реализовать один или несколько интерфейсов. Они указываются после имени структуры и отделяются запятыми. Как и у классов, членами структур могут быть методы, поля, индексаторы, свойства, операторные методы и события. Структуры могут также определять конструкторы, но не деструкторы. Однако для структуры нельзя определить конструктор по умолчанию (без параметров). Дело в том, что конструктор по умолчанию автоматически определяется для всех структур, и его изменить нельзя. Поскольку структуры не поддерживают наследования, члены структуры нельзя определять с использованием модификаторов abstract, virtual или protected.

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

// Демонстрация использования структуры.

using System;

// Определениеструктуры,

struct Book {

public string author;

public string title;

public int copyright;

public Book(string a, string t, int c) {

author = a;

title = t;

copyright = c;

// Демонстрируем использование структуры Book,

classStructDemo {

public static void Main() {

Book bookl = new Book("Herb Schildt",

"C# A Beginner's Guide", 2001); // Вызов явнозаданного конструктора.

Book book2 = new Book(); // Вызов конструктора по умолчанию.

Book ЬоокЗ; // Создание объекта без вызова конструктора.

Console.WriteLine(bookl.title + ", автор " + bookl.author + 11, (с) " + bookl. copyright );

Console.WriteLine();

if(book2.title == null)

Console.WriteLine("Член book2.title содержит null.");

// Теперь поместим в структуру book2 данные.

book2.title = "Brave New World";

book2.author = "Aldous Huxley";

book2.copyright = 1932;

Console.Write("Теперь структура book2 содержит:\n " ) ;

Console.WriteLine(book2.title + ", автор " + book2.author + ", (c) " + book2.copyright);

Console.WriteLine();

// Console.WriteLine(ЬоокЗ.title); // Ошибка: сначала необходима инициализация.

ЬоокЗ.title = "Red Storm Rising";

Console.WriteLine(ЬоокЗ.title); // Теперьвсе Ok!

Вот результаты выполнения этой программы:

С# A Beginner's Guide, автор Herb Schildt, (с) 2001

Член book2.title содержит null.

Теперь структура book2 содержит:

Brave New World, автор Aldous Huxley, (с) 1932

Red Storm Rising

Как видно из результатов выполнения этой программы, структура может быть создана либо с помощью оператора new, который вызывает соответствующий конструктор, либо простым объявлением объекта. При использовании оператора new поля структуры будут инициализированы, причем это сделает либо конструктор по умолчанию (он инициализирует все поля значениями по умолчанию), либо конструктор, определенный пользователем. Если оператор new не используется, как в случае объекта bоокЗ, созданный таким образом объект остается неинициализированным, и его поля должны быть установлены до начала использования.

При присваивании одной структуры другой создается копия этого объекта. Это — очень важное отличие struct-объекта от с lass-объекта. Как упоминалось выше, присваивая одной ссылке на класс другую, вы просто меняете объект, на который ссылается переменная, стоящая с левой стороны от оператора присваивания. А присваивая одной struct-переменной другую, вы создаете копию объекта, указанного с правой стороны от оператора присваивания. Рассмотрим, например, следующую программу:

// Копирование структуры.

using System;

// Определяемструктуру,

struct MyStruct { public int x; }

// Демонстрируем присваивание структуры,

class StructAssignment {

public static void Main() {

MyStruct a;

MyStruct b;

a.x = 10;

b.x = 20;

Console .WriteLine ("a.x {0}, b.x {I}11, a.x, b.x);

a - b;

b.x = 30;

Console.WriteLine("a.x {0}, b.x {1}", a.x, b.x);

Эта программа генерирует следующие результаты.

а . х 10, b . x 20

a . x 20, b . x 30

Как подтверждают результаты выполнения этой программы, после присваивания а = b; структурные переменные а и b по-прежнему не зависят одна от другой. Другими словами, переменная а никак не связана с переменной Ь, если не считать, что переменная а содержит копию значения переменной Ь. Будь а и b ссылками на классы, все обстояло бы по-другому. Рассмотрим теперь class-версию предыдущей программы.

// Копирование класса.

using System;

// Определяем класс,

class MyClass { public int x; }

// Теперь покажем присваивание объектов класса.

class ClassAssignment {

public static void Main() {

MyClass a = new MyClassO;

MyClass b = new MyClassO;

a.x = 10;

b.x = 20;

Console.WriteLine("a.x {0}, b.x {1}", a.x, b.x);

a = b;

b.x = 30;

Console.WriteLine("a.x {0}, b.x {1}", a.x, b.x);

Вот какие результаты получены при выполнении этой программы:

a . x 10, b . x 20

a . x 30, b . x 30

Как видите, после присваивания объекта b переменной а обе переменные ссылаются на один и тот же объект, т.е. на тот, на который изначально ссылалась переменная b.

Зачем нужны структуры вы могли бы выразить удивление, почему С# включает тип struct , если, казалось бы, он представляет собой "слаборазвитую" версию типа class. Ответ следует искать в эффективности и производительности. Поскольку структуры — это типы значений, они обрабатываются напрямую, а не через ссылки. Таким образом, тип struct не требует отдельной ссылочной переменной. Это означает, что при использовании структур расходуется меньший объем памяти. Более того, благодаря прямому доступу к структурам, при работе с ними не снижается производительность, что имеет место при доступе к объектам классов. Поскольку классы — ссылочные типы, доступ к их объектам осуществляется через ссылки. Такая косвенность увеличивает затраты системных ресурсов при каждом доступе. Структуры этим не страдают. В общем случае, если вам нужно хранить небольшую группу связанных данных, но не нужно обеспечивать наследование и использовать другие достоинства ссылочных типов, тип struct может оказаться более предпочтительным вариантом.

Перечисления

Перечисление (enumeration) — это множество именованных целочисленных констант. Ключевое слово enum объявляет перечислимый тип. Формат записи перечисления таков:

enum имя {список_перечисления);

Здесь с помощью элемента имя указывается имя типа перечисления. Элемент список_перечисления представляет собой список идентификаторов, разделенных запятыми.

Рассмотрим пример. В следующем фрагменте кода определяется перечисление

apple, которое содержит список названий различных сортов яблок. I enum apple { Jonathan, GoldenDel, RedDel, Winsap, Cortland, Mclntosh } ;

Здесь важно понимать, что каждый символ списка перечисления означает целое число, причем каждое следующее число (представленное идентификатором) на единицу больше предыдущего. Поскольку значение первого символа перечисления равно нулю, следовательно, идентификатор Jonathan имеет значение 0, GoldenDel — значение 1 и т.д.

Константу перечисления можно использовать везде, где допустимо целочисленное значение. Однако между типом enum и встроенным целочисленным типом неявные преобразования не определены, поэтому при необходимости должна использоваться явно заданная операция приведения типов. В случае преобразования одного типа перечисления в другой также необходимо использовать приведение типов.

К членам перечисления доступ осуществляется посредством имени типа и оператора "точка". Например, при выполнении инструкции

I Console.WriteLine(apple.RedDel + " имеет значение " + I (int)apple.RedDel);

будет отображено следующее.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]