Динамічна ідентифікація типів. Оператори 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();

// спробувати виконати приведення типів, якщо можливо
// в objB заноситься результат приведення об'єкту objA
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

 


Зв’язані теми