Рефлексія типів. Отримання метаданих типу. Простір імен System.Reflection. Клас System.Type. Способи отримання інформації про тип

Рефлексія типів. Отримання метаданих типу. Простір імен System.Reflection. Клас System.Type. Способи отримання інформації про тип. Оператор typeof


Зміст


1. З допомогою яких інструментів Microsoft Visual Studio можна проаналізувати основну інформацію про збірку?

У склад Microsoft Visual Studio входять спеціальні утиліти ildasm.exe та reflector.exe. Ці утиліти дозволяють аналізувати:

  • CIL-код збірки;
  • метадані типів, що реалізовані у збірці;
  • маніфест збірки для будь-якого двійкового файлу .NET.

Крім того, у Microsoft Visual Studio цю саму інформацію можна отримати програмно з допомогою простору імен System.Reflection.

 

2. Які типи можна повністю описувати з допомогою метаданих?

З допомогою метаданих можна повністю описувати наступні типи:

  • класи (class);
  • інтерфейси (interface);
  • структури (struct);
  • зчислення (перечислення) (enum);
  • делегати (delegate).

 

3. Що таке рефлексія в .NET?

Рефлексія (reflection) в .NET – це процес виявлення метаданих (типів) під час виконання програми у вигляді об¢єктної моделі. Іншими словами рефлексія – це засіб для отримання відомостей про тип даних. Рефлексія дозволяє отримати таку саму інформацію про метадані типів, які можна отримати утилітою ildasm.exe (IL Disassembler).

Рефлексія дозволяє отримувати перелік усіх типів, які містяться всередині:

  • виконавчого модуля *.exe;
  • файлу збірки *.dll;
  • файлів з розширеннями *.mod та *.mdl у випадку багатофайлової збірки.

 

4. Яка перевага застосування рефлексії у програмах?

З допомогою засобів рефлексії (клас System.Type) можна отримати дані про типи та їх характеристики в збірці під час виконання програми. Це дає такі переваги:

  • можна створювати типи та викликати їх методи без попередніх знань про імена, що містяться у цих типах. Це реалізує так звана динамічна ідентифікація типів;
  • не потрібно на етапі компіляції знати інформацію про тип, з якого будуть витягуватись метадані. Достатньо знати тільки назву типу. Назва типу задається в рядку String(), який є загальнодоступний у будь-якому місці;
  • представлення типів, що витягуються зі збірки, у вигляді зручної об’єктної моделі.

 

5. У якій збірці та якому просторі імен реалізована рефлексія в C# .NET?

У C# .NET рефлексія реалізована в просторі імен System.Reflection, який поставляється у складі збірки mscorlib.dll.

Щоб у програмі на C# використовувати рефлексію, потрібно підключити простір імен System.Reflection

using System.Reflection;

 

6. Перелік та призначення найбільш важливих типів з простору імен Reflection

Простір System.Reflection має багато типів для реалізації рефлексії. Однак, найбільш важливим є наступні:

  • Assembly – абстрактний клас. Він містить статичні методи роботи зі збіркою. Ці методи дозволяють завантажувати збірку
  • AssemblyName – це є клас, який містить інформацію що використовується для ідентифікації збірки. Наприклад: номер версії збірки, інформація про культуру тощо;
  • EventInfo – абстрактний клас. Містить інформацію про задану подію;
  • FieldInfo – абстрактний клас. Може містити інформацію про задані члени даних класу;
  • MemberInfo – абстрактний клас. Містить інформацію про загальну поведінку для класів (типів) EventInfo, FieldInfo, MethodInfo та PropertyInfo;
  • MethodInfo – абстрактний клас. Містить інформацію про заданий метод;
  • Module – абстрактний клас. Дозволяє отримати інформацію до заданого модуля у випадку багатофайлової збірки;
  • ParameterInfo – клас, який містить інформацію про заданий параметр в заданому методі;
  • PropertyInfo – абстрактний клас. Містить інформацію про задану властивість.

Щоб отримати інформацію про заданий клас, використовуються методи класу System.Type, які повертають результат у вигляді масивів або окремих класів вищенаведених типів (Assembly, MethodInfo, ParameterInfo і т.д.).

 

7. Яке основне призначення класу System.Type? Як клас System.Type пов’язаний з рефлексією?

Клас System.Type є корисний, якщо потрібно вивчати метадані деякого типу (класу, інтерфейсу, структури, зчислення, делегату). Клас System.Type інкапсулює в собі тип даних. Методи класу System.Type повертають типи з простору імен System.Reflection.

Клас System.Type є основою рефлексії. Завдяки методам класу System.Type можна визначати інформацію про використовувані типи під час виконання програми. Потім цю інформацію можна аналізувати, обробляти в залежності від поставленої задачі.



 

8. Як отримати інформацію про тип у програмі? Які є способи програмного отримання інформації про тип?

В C# .NET існує три способи програмного отримання інформації про тип:

  1. Використання методу System.Object.GetType(). У цьому випадку метод повертає метадані поточного об’єкту типу (див. п. 9).
  2. Використання засобу typeof(). У цьому випадку створювати об’єкт типу (наприклад, об’єкт класу) не потрібно. Достатньо мати оголошений тип (див. п. 10).
  3. Використання статичного методу System.Type.GetType(). Цей підхід не потребує оголошення класу (типу), інформацію про який потрібно отримати (див. п. 11). Достатньо знати тільки ім’я цього типу.

 

9. Яким чином отримати інформацію про тип з допомогою методу System.Object.GetType()? Приклад

Типом може бути клас, інтерфейс, структура, перечислення, делегат. Цей спосіб потребує отримання інформації про досліджуваний тип на етапі компіляції. Тобто, оголошення типу має бути відоме до компіляції.

Наприклад, щоб використовувати цей спосіб для класу потрібно:

  • спочатку створити об’єкт класу з допомогою оператора new;
  • викликати метод класу obj.GetType(), де obj – ім’я об’єкту даного класу.

Попередньо, на початку програмного модуля має бути підключений простір імен System.Reflection:

using System.Reflection;

 

Приклад. Задано оголошення класу MathFunctions, який містить 3 методи та 3 члени даних класу:

  • метод Min2() – знаходить мінімум між двома значеннями;
  • метод Min3() – знаходить мінімум між трьома значеннями;
  • метод Max2() – знаходить максимум між двома значеннями;
  • внутрішні члени даних класу з іменами a, b, c.
class MathFunctions
{
    public int a, b, c; // члени даних класу

    // мінімум між 2-ма значеннями
    public int Min2(int a, int b)
    {
        if (a < b) return a;
        return b;
    }

    // мінімум між 3-ма значеннями
    public int Min3(int a, int b, int c)
    {
        int min = a;
        if (min > b) min = b;
        if (min > c) min = c;
        return min;
    }

    // максимум між 2-ма значеннями
    public int Max2(int a, int b)
    {
        if (a < b) return b;
        return a;
    }
}

Отримати перелік методів класу MathFunctions з програмним шляхом можна наступним чином

class Program
{
    static void Main(string[] args)
    {
        // отримати значення типу
        MathFunctions mf = new MathFunctions();
        Type tp = mf.GetType();

        // взяти перелік методів з класу MathFunctions
        MethodInfo[] mi = tp.GetMethods();

        // отримати назви методів - спрощений варіант
        string s1 = mi[0].Name; // s1 = "Min2"
        string s2 = mi[1].Name; // s2 = "Min3"
        string s3 = mi[2].Name; // s3 = "Max2"

        Console.WriteLine("Method1 = {0}", s1); // Method1 = Min2
        Console.WriteLine("Method2 = {0}", s2); // Method2 = Min3
        Console.WriteLine("Method3 = {0}", s3); // Method3 = Max2
    }
}

У даному прикладі клас MathFunctions може бути реалізований

  • у тому самому просторі імен;
  • в іншій збірці. У цьому випадку збірка з оголошеним класом має бути підключена в розділі References та директивою using.

 

10. Яким чином отримати інформацію про тип з допомогою засобу typeof? Приклад

Типом може бути клас, інтерфейс, структура, перечислення, делегат. Щоб отримати інформацію про тип з допомогою засобу typeof, так само як і в попередньому пункті, потрібно мати інформацію про тип на етапі компіляції.

Наприклад, щоб використати цей спосіб для класу, не потрібно попередньо створювати об’єкт класу. В загальному рядок, що отримує інформацію про деякий клас, має наступний вигляд:

Type tp = typeof(ClassName);

де ClassName – ім’я деякого класу, для якого створюється об’єкт з іменем tp, що містить інформацію про цей клас.

Попередньо, на початку програмного модуля має бути підключений простір імен System.Reflection:

using System.Reflection;

Приклад. Нехай задано клас MathFunctions, оголошений в п. 9. Клас містить 3 методи з іменами Min2(), Min3(), Max2() та 3 внутрішні змінні з іменами a, b, c.

Тоді, отримання інформації про методи та внутрішні змінні класу з використанням засобу typeof() може бути наступним:

...

class Program
{
    static void Main(string[] args)
    {
        // отримати значення типу
        Type tp = typeof(MathFunctions);

        // взяти перелік методів з класу MathFunctions
        MethodInfo[] mi = tp.GetMethods();

        // отримати назви методів
        string m1 = mi[0].Name; // m1 = "Min2"
        string m2 = mi[1].Name; // m2 = "Min3"
        string m3 = mi[2].Name; // m3 = "Max2"

        // взяти перелік внутрішніх даних класу
        FieldInfo[] fi = tp.GetFields();

        string f1 = fi[0].Name; // назва внутрішньої змінної a, f1 = "a"
        string f2 = fi[1].Name; // f2 = "b"
        string f3 = fi[2].Name; // f3 = "c"

        Console.WriteLine("Method1 = {0}", m1); // Method1 = Min2
        Console.WriteLine("Method2 = {0}", m2); // Method2 = Min3
        Console.WriteLine("Method3 = {0}", m3); // Method3 = Max2

        Console.WriteLine("Field1 = {0}", f1); // Field1 = a
        Console.WriteLine("Field2 = {0}", f2); // Field2 = b
        Console.WriteLine("Field3 = {0}", f3); // Field3 = c
    }
}

...

Важливо: щоб отримати інформацію про внутрішні змінні та методи класу, ці змінні та методи мають бути загальнодоступними, тобто, оголошені з модифікатором доступу public. В іншому випадку, приховані внутрішні змінні та приховані методи є невидимі.

 

11. Яким чином отримати інформацію про тип з допомогою System.Type.GetType()? Приклад

Типом може бути клас, інтерфейс, структура, перечислення, делегат. Інформація про тип (клас, інтерфейс, структура і т.д.) отримується з допомогою статичного методу GetType() класу System.Type. Метод GetType() отримує вхідним параметром рядок типу System.String. Цей рядок повинен містити ім’я збірки та ім’я типу (класу, інтерфейсу, …) про який потрібно визначити інформацію.

Приклад 1. Задано збірку з іменем MyAssembly. У збірці оголошено клас з іменем MyClass. Тоді рядок, що повертає клас, який буде реалізовувати збірку має вигляд

...

System.Type T = GetType("MyAssembly.MyClass");

...

Після цього, екземпляр (об’єкт) T буде містити інформацію про клас MyClass.

Приклад 2. Демонструється використання System.Type.GetType() для доступу до класу MathFunctions,описаного в п. 9. У даном прикладі передбачається, що клас MathFunctions оголошується всередині простору імен TrainReflections1. Програмний код усього модуля, що демонструє використання класу для консольного додатку наступний:

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

// підключити простір імен System.Reflection
using System.Reflection;

namespace TrainReflection1
{
    class MathFunctions
    {
        public int a, b, c; // члени даних класу

        // мінімум між 2-ма значеннями
        public int Min2(int a, int b)
        {
            if (a < b) return a;
            return b;
        }

        // мінімум між 3-ма значеннями
        public int Min3(int a, int b, int c)
        {
            int min = a;
            if (min > b) min = b;
            if (min > c) min = c;
            return min;
        }

        // максимум між 2-ма значеннями
        public int Max2(int a, int b)
        {
            if (a < b) return b;
            return a;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            // отримати значення типу
            Type tp = null;
            tp = Type.GetType("TrainReflection1.MathFunctions");

            // взяти перелік методів з класу MathFunctions
            MethodInfo[] mi = tp.GetMethods();

            // отримати назви методів
            string m1 = mi[0].Name; // m1 = "Min2"
            string m2 = mi[1].Name; // m2 = "Min3"
            string m3 = mi[2].Name; // m3 = "Max2"

            // взяти перелік внутрішніх даних класу
            FieldInfo[] fi = tp.GetFields();
            string f1 = fi[0].Name; // f1 = "a"
            string f2 = fi[1].Name; // f2 = "b"
            string f3 = fi[2].Name; // f3 = "c"

            Console.WriteLine("Method1 = {0}", m1); // Method1 = Min2
            Console.WriteLine("Method2 = {0}", m2); // Method2 = Min3
            Console.WriteLine("Method3 = {0}", m3); // Method3 = Max2

            Console.WriteLine("Field1 = {0}", f1); // Field1 = a
            Console.WriteLine("Field2 = {0}", f2); // Field2 = b
            Console.WriteLine("Field3 = {0}", f3); // Field3 = c
        }
    }
}

 


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