5 марта 2011 г.

Разница между явным приведением типа и оператором as

Пусть у нас есть несколько простейших классов, соответствующих геометрическим фигурам: круг, квадрат и прямоугольник.


public class Circle
{
public int radius;

public Circle(int radius)
{
this.radius = radius;
}
}

У круга из параметров есть только радиус. Понятно, что нужно ещё хранить информацию о его центре, но для упрощения модели мы это опустим.

public class Square
{
protected int width;

public Square(int width)
{
this.width = width;
}
}

У квадрата есть параметр ширина.

public class Rectangle : Square
{
private int height;

public Rectangle(int width, int height)
: base(width)
{
this.height = height;
}
}

У прямоугольника есть параметры ширина и высота. Этот класс логичнее наследовать от квадрата, т.к. квадрат - это прямоугольник, у которого ширина равна высоте. Обратите внимание, что в конструкторе сначала вызывается конструктор базового класса для установки ширины, т.к. за ширину отвечает базовый класс.

Теперь рассмотрим ситуацию, когда нам понадобилось упаковать экземпляры круга и прямоугольника в общий для них класс, которым является класс object. Это общий для всех классов в платформе .NET. Классический принцип объектно-ориентированного программирования (ООП), когда мы можем хранить ссылку на производный класс в ссылке базового класса.


// прямоугольник 3 x 6
object Object1 = new Rectangle(3, 6);
// круг с радиусом 4
object Object2 = new Circle(4);

Теперь нам понадобилось поместить эти объекты в объекты типа Square. Традиционный подход - это явное приведение. Но что, если нам заранее неизвестно, совместим ли приводимый объект с классом квадрат?

Square SquareFromRectangle = (Square)Object1;
Square SquareFromCircle = (Square)Object2; // здесь возникнет исключение

Первая строка выполниться успешно, а вторая вызовет исключение InvalidCastException. Логично, ведь в Object2 у нас лежит круг, который, конечно же, несовместим с квадратом. Так вот чтобы в процессе выполнения программы не происходили подобные ситуации, воспользуемся оператором as. Перед этим словом указывается имя экземпляра исходного объекта, после него - целевой тип, на который он возвратит ссылку. Если объект является наследником Square, то мы получим соответствующую ссылку, а если нет - получим null без возникновения исключения. Поэтому следующий код сработает без ошибок:

Square SquareFromRectangle = Object1 as Square;
Square SquareFromCircle = Object2 as Square;

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

Комментариев нет:

Отправить комментарий