// Эта программа не подлежит компиляции.
using System;
class X
{
int a;
public X(int i) { a = i; }
}
class Y
{
int a;
public Y(int i) { a = i; }
}
class IncompatibleRef
{
static void Main()
{
X x = new X(10);
X x2;
Y y = new Y(5);
x2 = x; //верно, поскольку оба объекта относятся к одному и тому же типу
x2 = y; //ошибка, поскольку это разнотипные объекты
}
}
Несмотря на то что классы X и Y в данном примере совершенно одинаковы по своей структуре, ссылку на объект типа Y нельзя присвоить переменной ссылки на объект типа X, поскольку типы у них разные.
Вообще говоря, переменная ссылки на объект может ссылаться только на объект своего типа.
Но из этого принципа строгого соблюдения типов в C# имеется одно важное исключение: переменной ссылки на объект базового класса может быть присвоена ссылка на объект любого производного от него класса.
// По ссылке на объект базового класса можно обращаться
// к объекту производного класса.
using System;
class X
{
public int a;
public X(int i)
{
a = i;
}
}
class Y: X
{
public int b;
public Y(int i, int j)
: base(j)
{
b = i;
}
}
class BaseRef
{
static void Main()
{
X x = new X(10);
|
|
X x2;
Y у = new Y(5, 6);
x2 = x; // верно, поскольку оба объекта относятся к одному и тому же типу
Console.WriteLine("х2.а: " + x2.a);
x2 = у; // тоже верно, поскольку класс Y является производным от класса X
Console.WriteLine("х2.а: " + x2.a);
// ссылкам на объекты класса X известно только о членах класса X
x2.a = 19; // верно
// х2.b = 27; // неверно, поскольку член b отсутствует у класса X
}
}
Следует особо подчеркнуть, что доступ к конкретным членам класса определяется типом переменной ссылки на объект, а не типом объекта, на который она ссылается. Это означает, что если ссылка на объект производного класса присваивается переменной ссылки на объект базового класса, то доступ разрешается только к тем частям этого объекта, которые определяются базовым классом. Именно поэтому переменной х2 недоступен член b класса Y, когда она ссылается на объект этого класса. И в этом есть
своя логика, поскольку базовому классу ничего не известно о тех членах, которые добавлены в производный от него класс.
Один из самых важных моментов для присваивания ссылок на объекты производного класса переменным базового класса наступает тогда, когда конструкторы вызываются в иерархии классов. Как вам должно быть уже известно, в классе нередко определяется конструктор, принимающий объект своего класса в качестве параметра. Благодаря этому в классе может быть сконструирована копия его объекта. Этой особенностью можно выгодно воспользоваться в классах, производных от такого класса. В качестве примера рассмотрим очередные варианты классов TwoDShape и Triangle. В оба класса добавлены конструкторы, принимающие объект в качестве параметра.
|
|
// Передать ссылку на объект производного класса
// переменной ссылки на объект базового класса.
using System;
class TwoDShape
{
double pri_width;
double pri_height;
// Конструктор по умолчанию.
public TwoDShape()
{
Width = Height = 0.0;
}
// Конструктор для класса TwoDShape.
public TwoDShape(double w, double h)
{
Width = w;
Height = h;
}
// Сконструировать объект равной ширины и высоты.
public TwoDShape(double x)
{
Width = Height = x;
}
// Сконструировать копию объекта TwoDShape.
public TwoDShape(TwoDShape ob)
{
Width = ob.Width;
Height = ob.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()
{
Style = "null";
}
// Конструктор для класса Triangle.
public Triangle(string s, double w, double h)
: base(w, h)
{
Style = s;
}
// Сконструировать равнобедренный треугольник.
public Triangle(double x)
: base(x)
{
Style = "равнобедренный";
}
// Сконструировать копию объекта типа Triangle.
public Triangle(Triangle ob)
: base(ob)
{
Style = ob.Style;
}
// Возвратить площадь треугольника.
public double Area()
{
return Width * Height / 2;
}
// Показать тип треугольника.
public void ShowStyle()
{
Console.WriteLine("Треугольник " + Style);
}
}
class Shapes7
{
static void Main()
{
Triangle t1 = new Triangle("прямоугольный", 8.0, 12.0);
// Сделать копию объекта t1.
Triangle t2 = new Triangle(t1);
Console.WriteLine("Сведения об объекте t1: ");
t1.ShowStyle();
t1.ShowDim();
Console.WriteLine("Площадь равна " + t1.Area());
Console.WriteLine();
Console.WriteLine("Сведения об объекте t2: ");
t2.ShowStyle();
t2.ShowDim();
Console.WriteLine("Площадь равна " + t2.Area());
}
}
Сведения об объекте t1:
Треугольник прямоугольный
Ширина и высота равны 8 и 12
Площадь равна 48
Сведения об объекте t2:
Треугольник прямоугольный
Ширина и высота равны 8 и 12
Площадь равна 48
Обратите особое внимание на следующий конструктор класса Triangle:
public Triangle(Triangle ob): base(ob)
{
Style = ob.Style;
}
Он принимает объект типа Triangle в качестве своего параметра и передает его(с помощью ключевого слова base) следующему конструктору класса TwoDShape.
public TwoDShape(TwoDShape ob)
{
Width = ob.Width;
Height = ob.Height;
}