Динамическая идентификация типов. Операторы is, as. Примеры

Динамическая идентификация типов. Операторы is, as. Примеры


Содержание


1. Для чего нужна динамическая идентификация типов (RTTI – run-time type information)? Преимущества использования

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

  • если классы наследуют друг друга или образовывают иерархию, то при наличии ссылки на базовый класс, можно определить тип объекта по этой ссылке (см. п. 5);
  • если нужно заведомо определить результат операции приведения типов для того, чтобы избегнуть генерирования исключительной ситуации в случае ошибочного приведения;
  • динамическая идентификация типов эффективно используется в рефлексии. Более подробно о рефлексии описывается здесь.

 

2. Какие ключевые слова (операторы) введенны в языке C# для обеспечения динамической идентификации типов?

Для обеспечения динамической идентификации типов в языке C# введенны три оператора:

  • оператор is – проверяет, совпадает ли тип выражения с заданным типом данных;
  • оператор as – предназначен для избежания возникновения исключительной ситуации в случае неудачного приведения типов;
  • оператор typeof. – используется для получения информации о заданном типе (классе). Более подробно об операторе typeof описывается здесь.

 

3. Общая форма оператора is

Общая форма оператора is имеет следующий вид:

выражение is тип

где

  • выражение есть обозначением некоторого выражения, описывающего объект;
  • тип – некоторый тип, с которым сравнивается тип выражения. Если выражение и тип имеют одинаковый тип данных, то результат операции будет true. В противном случае, результат операции is будет false.

Чаще всего оператор is используется в условии оператора if. В этом случае, общая форма оператора if с оператором is имеет следующий вид

if (выражение is тип)
{
// операции, которые нужно выполнить, если типы совпадают
// ...
}
else
{
// операции, которые нужно выполнить, если типы не совпадают
// ...
}

 

4. Примеры использования оператора is для базовых типов и выражений

Пример 1. Фрагмент кода, демонстрирующие использование оператора is для переменных базовых типов int, float.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp5
{
class Program
{
static void Main(string[] args)
{
// динамическая идентификация типов
int a, b;
float x;

// перед использованием переменные должны быть инициализированы
a = 25;
b = 33;
x = 3.9f;

// проверка, есть ли a типа int
if (a is int)
Console.WriteLine("Переменная a есть типа int.");
else
Console.WriteLine("Переменная a не имеет типа int");

// проверка, есть ли переменная b типа float
if (b is float)
Console.WriteLine("Переменная b имеет тип float");
else
Console.WriteLine("Переменная b не имеет тип float");

// проверка, есть ли переменная x типа double
if (x is double)
Console.WriteLine("Переменная x есть типа double");
else
Console.WriteLine("Переменная x не имеет тип double");
}
}
}

Как видно из вышеприведенного примера, для того чтобы использовать оператор is, выражение может иметь какое-то значение. Если в начале функции Main() убрать строки присваивания значений переменным a, b, x

...

a = 25;
b = 33;
x = 3.9f;

...

то компилятор выдаст ошибку наподобие

Use of unassigned local variable a
Use of unassigned local variable b
Use of unassigned local variable x

Пример 2. Фрагмент кода, демонстрирующий использование оператора is для определения типа вычисляемого выражения.

// динамическая идентификация типов
// определение типа выражения
float x;

// перед использованием, переменные должны быть иницилизированы
x = 3.9f;

// проверка, есть ли выражение типа double
if ((2.5 + x) is double)
Console.WriteLine("Выражение есть типа double");
else
Console.WriteLine("Выражение не имеет тип double");
// Результат - "Выражение есть типа double"

// Определение типа выражения 2*2
if ((2 * 2) is int)
Console.WriteLine("Тип int");
else
if ((2 * 2) is uint)
Console.WriteLine("Тип uint");
else
if ((2 * 2) is long)
Console.WriteLine("Тип long");
else
if ((2 * 2) is byte)
Console.WriteLine("Тип byte");
// Результат - "Тип int"

 

5. Как работает оператор is в случае наследования классов? Примеры

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

Пример 1. Пусть заданы 2 класса, которые образовывают иерархию. Класс BaseClass есть базовой. Класс DerivedClass есть производным от класса BaseClass. С помощью оператора is определяется принадлежность объекта класса заданному типу и совместимость объекта класса с заданным типом.

// базовый класс
class BaseClass
{
public int d;
}

// производный класс от базового класса
class DerivedClass:BaseClass
{
public int dd;
}

Ниже демонстрируется использование объектов базового и производного классов в сочетании с оператором is

// создание объектов классов BaseClass и DerivedClass
BaseClass bc = new BaseClass();
DerivedClass dc = new DerivedClass();

// использование объектов bc и dc
if (bc is BaseClass)
Console.WriteLine("Объект bc есть типа BaseClass");
if (bc is DerivedClass)
Console.WriteLine("Объект bc есть совместим с DerivedClass");
if (dc is BaseClass)
Console.WriteLine("Объект dc есть совместим с BaseClass");
if (dc is DerivedClass)
Console.WriteLine("Объект dc есть типа DerivedClass");

В результате выполнения вышеприведенного кода на экран будет выведено

Объект bc есть типа BaseClass
Объект dc есть совместим с BaseClass
Объект dc есть типа DerivedClass

В строке

...
if (bc is DerivedClass)
Console.WriteLine("Объект bc есть совместим с DerivedClass");
...

значение условия в операторе if равное false, так как объект базового класса не может быть совместим с производным классом. Однако, наоборот, объект производного класса есть совместим с базовым, так как, производный класс расширяет возможности базового класса.

Пример 2. Заданы 3 класса с именами A, B, C, которые образовывают иерархию: класс A есть базовым, класс B наследует класс A, класс C наследует класс B. Объявление классов имеет следующий вид:

// базовый класс
class A
{
public int a;
}

// класс B наследует A
class B : A
{
public int b;
}

// класс C наследует B
class C: B
{
public int c;
}

Ниже приводится пример, который определяет тип объекта класса B и его совместимость с типами A, C.

// объект класса B
B objB = new B();

if (objB is A)
Console.WriteLine("objB есть совместим с типом A");
if (objB is B)
Console.WriteLine("objB есть типа B");
if (objB is C)
Console.WriteLine("obJB есть совместим с типом C");

В результате выполнения вышеприведенного кода, на экран будет выведено сообщение:

objB есть совместим с типом A
objB есть типа B

 

6. Оператор as. Общая форма

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

выражение as тип

где выражение – отдельное выражение, которое приводится к типу тип. Если выражение приводится к типу тип корректно, то результатом операции as есть ссылка на тип. В противном случае результатом операции as есть пустая ссылка null.



С учетом вышесказанного, общая форма оператора as, который размещается в правой части оператора присваивания, имеет вид:

objName = выражение as тип;

где objName – имя объекта, который будет содержать результат. Результатом может быть null или ссылка на объект класса тип.

 

7. Пример использования оператора as

В примере используется оператор as для предшествующей проверки корректности приведения типа без вызова исключительной ситуации.
Пусть объявлены классы A, B, C которые образовывают иерархию

// базовый класс
class A
{
public int a;
}

// класс B наследует A
class B : A
{
public int b;
}

// класс C наследует B
class C: B
{
public int c;
}

Ниже продемонстрировано использование оператора as для классов A, B, C

// объекты классов A, B, C
A objA = new A();
B objB = new B();
C objC = new C();

// попробовать выполнить приведение типов, если возможно
// в obj заносится результат приведения объекта obj
objB = objA as B;

if (objB == null)
Console.WriteLine("Невозможно привести objA к типу B");
else
Console.WriteLine("Можно приводить objA к типу B");

// еще одна попытка привести objC к типу B, результат в objB
objB = objC as B;

// вывод результата
if (objB == null)
Console.WriteLine("Невозможно привести objC к типу B");
else
Console.WriteLine("Можно приводить objC к типу B");

В результате выполнения вышеприведенного кода, на экран будет выведено

Невозможно привести objA к типу B
Можно приводить objC к типу B

 

8. Каким образом оператор as можно заменить оператором is? Пример

В данном примере происходит замена оператора as с помощью оператора is. Сначала выполняется проверка на допустимость приведения типов, а уже потом происходит приведение.
Пусть заданы три класса A, B, C которые имеют такое объявление

// базовый класс
class A
{
public int a;
}

// класс B наследует A
class B : A
{
public int b;
}

// класс C наследует B
class C : B
{
public int c;
}

Ниже демонстрируется фрагмент кода, который использует оператор is для определения допустимости приведения типа. Происходит проверка оператором if.

// объекты классов A, B, C
A objA = new A();
B objB = new B();
C objC = new C();

// попытка приведения к типу B объектов objA, objC
// с использованием оператора is
// проверка
if (objA is B)
objB = (B)objA;
else
objB = null;

if (objB == null)
Console.WriteLine("Невозможно привести objA к типу B");
else
Console.WriteLine("Можно приводить objA к типу B");

// так же проверка objC
if (objC is B)
objB = (B)objC;
else
objB = null;

if (objB == null)
Console.WriteLine("Невозможно привести ojbC к типу B");
else
Console.WriteLine("Можно приводить objC к типу B");

В результате выполнения вышеприведенного кода, на экран будет выведен следующий результат

Невозможно привести objA к типу B
Можно приводить objC к типу B

 


Связанные темы