Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
// Продемонстрировать применение модификатора доступа protected.using System;
class В { protected int i, j; // члены, закрытые для класса В, // но доступные для класса Dpublic void Set(int a, int b) { i = a; j = b;}public void Show() { Console.WriteLine(i + " " + j);}
}
class D : В { int k; // закрытый член // члены i и j класса В доступны для класса Dpublic void Setk() { k = i * j;}public void Showk() { Console.WriteLine(k);}
}
class ProtectedDemo { static void Main() { D ob = new D(); ob.Set(2, 3); // допустимо, поскольку доступно для класса D ob.Show(); // допустимо, поскольку доступно для класса D ob.Setk(); // допустимо, поскольку входит в класс D ob.Showk(); // допустимо, поскольку входит в класс D }}В данном примере класс В наследуется классом D, а его члены i и j объявлены какprotected, и поэтому они доступны для метода Setk(). Если бы члены i и j класса Вбыли объявлены как private, то они оказались бы недоступными для класса D, и приведенный выше код нельзя было бы скомпилировать.Аналогично состоянию public и private, состояние protected сохраняется зачленом класса независимо от количества уровней наследования. Поэтому когда производный класс используется в качестве базового для другого производного класса, любой защищенный член исходного базового класса, наследуемый первым производнымклассом, наследуется как защищенный и вторым производным классом.Несмотря на всю свою полезность, защищенный доступ пригоден далеко не длявсех ситуаций. Так, в классе TwoDShape из приведенного ранее примера требовалось,чтобы значения его членов Width и Height были доступными открыто, посколькунужно было управлять значениями, которые им присваивались, что было бы невозможно, если бы они были объявлены как protected. В данном случае более подходящим решением оказалось применение свойств, чтобы управлять доступом, а не предотвращать его. Таким образом, модификатор доступа protected следует применятьв том случае, если требуется создать член класса, доступный для всей иерархии классов, но для остального кода он должен быть закрытым. А для управления доступом кзначению члена класса лучше воспользоваться свойством.## Конструкторы и наследованиеВ иерархии классов допускается, чтобы у базовых и производных классов были своисобственные конструкторы. В связи с этим возникает следующий резонный вопрос:какой конструктор отвечает за построение объекта производного класса: конструкторбазового класса, конструктор производного класса или же оба? На этот вопрос можноответить так: конструктор базового класса конструирует базовую часть объекта, а конструктор производного класса — производную часть этого объекта. И в этом есть своялогика, поскольку базовому классу неизвестны и недоступны любые элементы производного класса, а значит, их конструирование должно происходить раздельно. В приведенных выше примерах данный вопрос не возникал, поскольку они опирались наавтоматическое создание конструкторов, используемых в C# по умолчанию. Но напрактике конструкторы определяются в большинстве классов. Ниже будет показано,каким образом разрешается подобная ситуация.Если конструктор определен только в производном классе, то все происходит оченьпросто: конструируется объект производного класса, а базовая часть объекта автоматически конструируется его конструктором, используемым по умолчанию. В качествепримера ниже приведен переработанный вариант класса Triangle, в котором определяется конструктор, а член Style делается закрытым, так как теперь он устанавливается конструктором.
// Добавить конструктор в класс Triangle.using System;
// Класс для двумерных объектов.class TwoDShape { double pri_width; double pr.i_height;// Свойства ширины и длины объекта.public double Width { get { return pri_width; } set { pri_width = value < 0 ? -value : value; }}public double Height { get { return pri_height; } set { pri_height = value < 0 ? -value : value; }}public void ShowDim() { Console.WriteLine("Ширина и длина равны " + Width + " и " + Height);}
}
// Класс для треугольников, производный от класса TwoDShape.class Triangle : TwoDShape { string Style;// Конструктор.public Triangle(string s, double w, double h) { Width = w; // инициализировать член базового класса Height = h; // инициализировать член базового класса Style = s; // инициализировать член производного класса}// Возвратить площадь треугольника.public double Area() { return Width * Height / 2;}// Показать тип треугольника.public void ShowStyle() { Console.WriteLine("Треугольник " + Style);}
}
class Shapes3 { static void Main() { Triangle t1 = new Triangle("равнобедренный", 4.0, 4.0); Triangle t2 = new Triangle("прямоугольный", 8.0, 12.0); Console.WriteLine("Сведения об объекте t1: "); t1.ShowStyle(); t1.ShowDim(); Console.WriteLine("Площадь равна " + t1.Area()); Console.WriteLine(); Console.WriteLine("Сведения об объекте t2: "); t2.ShowStyle(); t2.ShowDim(); Console.WriteLine("Площадь равна " + t2.Area());}
}В данном примере конструктор класса Triangle инициализирует наследуемыечлены класса TwoDShape вместе с его собственным полем Style.
Когда конструкторы определяются как в базовом, так и в производном классе, процесс построения объекта несколько усложняется, поскольку должны выполнятьсяконструкторы обоих классов. В данном случае приходится обращаться к еще одномуключевому слову языка С#: base, которое находит двоякое применение: во-первых, длявызова конструктора базового класса; и во-вторых, для доступа к члену базового класса,скрывающегося за членом производного класса. Ниже будет рассмотрено первое применение ключевого слова base.Вызов конструкторов базового класса
С помощью формы расширенного объявления конструктора производного классаи ключевого слова base в производном классе может быть вызван конструктор, определенный в его базовом классе. Ниже приведена общая форма этого расширенногообъявления:конструктор_производного_класса(список_параметров) : base(список_аргументов) { // тело конструктора}
где список_аргументов обозначает любые аргументы, необходимые конструкторув базовом классе. Обратите внимание на местоположение двоеточия.
Для того чтобы продемонстрировать применение ключевого слова base на конкретном примере, рассмотрим еще один вариант класса TwoDShape в приведеннойниже программе. В данном примере определяется конструктор, инициализирующийсвойства Width и Height. Затем этот конструктор вызывается конструктором классаTriangle.// Добавить конструктор в класс TwoDShape.using System;// Класс для двумерных объектов.class TwoDShape { double pri_width; double pri_height; // Конструктор класса TwoDShape. public TwoDShape(double w, double h) { Width = w; Height = h; } // Свойства ширины и высоты объекта. public double Width { get { return pri_width; } set { pri_width = value < 0 ? -value : value; } } public double Height { get { return pri_height; } set { pri_height = value < 0 ? -value : value; } } public void ShowDim() { Console.WriteLine("Ширина и высота равны " + Width + " и " + Height); }}// Класс для треугольников, производный от класса TwoDShape.class Triangle : TwoDShape { string Style; // Вызвать конструктор базового класса. public Triangle(string s, double w, double h) : base(w, h) { Style = s; } // Возвратить площадь треугольника. public double Area() { return Width * Height / 2; } // Показать тип треугольника. public void ShowStyle() { Console.WriteLine("Треугольник " + Style); }}class Shapes4 { static void Main() { Triangle t1 = new Triangle("равнобедренный", 4.0, 4.0); Triangle t2 = new Triangle("прямоугольный", 8.0, 12.0); Console.WriteLine("Сведения об объекте t1: "); t1.ShowStyle(); t1.ShowDim(); Console.WriteLine("Площадь равна " + t1.Area()); Console.WriteLine(); Console.WriteLine("Сведения об объекте t2: "); t2.ShowStyle(); t2.ShowDim(); Console.WriteLine("Площадь равна " + t2.Area()); }}