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.