C#. .NET. Метадані типів. Призначення та особливості використання

Метадані типів. Призначення та особливості використання. Необхідність в метаданих. Способи отримання інформації про тип





Зміст


Пошук на інших ресурсах:

1. Що таке метадані типів? Приклад, що демонструє необхідність використання метаданих типів

Після написання програми мовою C# відбувається її компіляція. У результаті компіляції створюється збірка, яка є базовою одиницею .NET. Під збіркою мається на увазі файл з розширенням *.dll або *.exe. Кожна створювана збірка має складові. Однією зі складових збірки є так звані метадані, з допомогою яких описуються типи, що були визначені в програмі. Наприклад, якщо в програмі реалізовано клас з іменем MyClass, то вважається що це є тип MyClass, і цей тип описується метаданими в скомпільованій збірці.

Метадані типів широко використовуються при взаємодії між програмами. Можна перерахувати наступні напрямки використання метаданих типів:

  • серіалізаця об’єктів;
  • розподілені додатки;
  • віддалена робота;
  • XML веб-сервіси;
  • міжмовна взаємодія в середовищі IDE;
  • інші застосування.

На рисунку 1 зображено приклад використання метаданих у випадку розподілених додатків. Розглядається випадок об’єднання комп’ютерів у локальну мережу та їх взаємодія. Нехай на сервері розміщується математичне програмне забезпечення, яке містить відповідний функціонал. З одного або декількох клієнтських комп’ютерів здійснюється запит на сервер для проведення обчислень, наприклад, розв’язок квадратного рівняння. Сервер отримує запит, виконує обчислення та повертає результат клієнту.
Для того, щоб клієнт та сервер могли між собою взаємодіяти, потрібна наявність спільної збірки. У нашому випадку це збірка ClassLibrary1.dll. Ця збірка містить інформацію про метод CalcEquation() обчислення квадратного рівняння. Клієнт та сервер використовують метадані збірки для отримання інформації (типи, назви методів, параметри методів тощо) про вміст збірки. Доступ до метаданих збірки здійснюється з допомогою механізму рефлексії.

C# .NET. Взаємодія між клієнтом та сервером з допомогою метаданих спільної збірки

Рисунок 1. Взаємодія між клієнтом та сервером з допомогою метаданих спільної збірки

 

2. Типи, що описуються метаданими

У C# .NET з допомогою метаданих описуються наступні типи:

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

 

3. Способи отримання інформації про тип

Інформацію про типи можна отримати наступними способами:

  • з допомогою утиліти ildasm.exe. Утиліта ildasm.exe використовує механізм рефлексії для доступу до елементів збірки з допомогою зчитування метаданих типів. Більш детально про роботу утиліти ildasm.exe описується тут;
  • програмним шляхом.

Щоб отримати метадані програмним шляхом можна використати один з трьох способів:

  • використати метод System.Object.GetType(). Цей спосіб дозволяє визначити метадані об’єкту;
  • використати оператор typeof(). Цей спосіб дозволяє визначити інформацію про тип не створюючи об’єкту;
  • використати метод System.Type.GetType(). Цей спосіб дозволяє визначити інформацію про тип на основі його імені (рядкового представлення) з допомогою метаданих.

Перші два способи програмного отримання метаданих реалізують механізм раннього зв’язування. При такому виді зв’язування, інформація про типи є відома у програмі під час компіляції.
Третій спосіб (з допомогою методу System.Type.GetType()) реалізує механізм пізнього зв’язування. У випадку пізнього зв’язування, на момент компіляції, інформація про типи невідома. Компілятор містить узагальнений код визначення типу на основі так званої рефлексії. Більш детально про особливості застосування рефлексії описується тут.

 

4. Отримання інформації про тип методом System.Object.GetType(). Приклад

Як відомо, базовим класом для всіх класів у .NET є клас object або System.Object. Щоб отримати метадані типів з допомогою засобів класу System.Object використовується метод GetType(), який має наступну загальну форму:

public System.Type GetType();

У найпростішому випадку, метод System.Object.GetType() застосовується до екземпляру класу за наступним зразком

MyClass mc = new MyClass();
Type tp = mc.GetType();

У результаті отримується екземпляр tp типу Type. Цей екземпляр містить усю необхідну інформацію про тип MyClass.

Приклад. У прикладі продемонстровано застосування механізму рефлексії для класу Complex. Реалізується так зване раннє зв’язування.
Нехай задано клас Complex, в якому є наступні елементи:

  • внутрішні поля re, im;
  • конструктори без параметрів та з двома параметрами;
  • властивості Re та Im для доступу до внутрішніх полів класу;
  • методи Add(), Sub() що реалізують операції додавання та віднімання комплексних чисел;
  • метод Print() виведення інформації про числа на екран.

 

using System;
using System.Reflection;

namespace ConsoleApp15
{

  // Клас, що реалізує операції над комплексними числами
  class Complex
  {
    // Public fields
    private double re, im;

    // Constructors
    public Complex(double _re, double _im)
    {
      re = _re; im = _im;
    }

    public Complex() : this(0, 0) { }

    // Properties
    public double Re
    {
      get { return re; }
      set { re = value; }
    }

    public double Im
    {
      get { return im; }
      set { im = value; }
    }

    // Methods
    public Complex Add(double re2, double im2)
    {
      return new Complex(re + re2, im + im2);
    }

    public Complex Sub(double re2, double im2)
    {
      return new Complex(re - re2, im - im2);
    }

    public void Print(string text)
    {
      Console.Write(text);
      Console.Write("{0:f2}", re);
      if (im > 0)
        Console.Write("+");
      Console.WriteLine("{0:f2}*j", im);
    }
  }

  class Program
  {

    static void Main(string[] args)
    {

      // Отримати інформацію про типи класу Complex
      // з допомогою System.Object.GetType()

      // 1. Оголосити екземпляр класу Complex
      Complex cm = new Complex(5, 8);

      // 2. З екземпляру отримати тип
      Type tp = cm.GetType(); // метод GetType() класу Object

      // 3. Отримати перелік методів класу Complex
      // Методи вибрати за наступними критеріями:
      // - тільки public-методи;
      // - тільки з екземпляру;
      // - тільки ті методи, що оголошені в класі Complex.
      MethodInfo[] mi = tp.GetMethods(
        BindingFlags.Public |
        BindingFlags.Instance |
        BindingFlags.DeclaredOnly
      );

      // 4. Вивести назви методів класу Complex на екран
      foreach (MethodInfo m in mi)
      {
        Console.WriteLine("{0}", m.Name);
      }

      Console.ReadKey();
    }
  }
}

Результат виконання програми

get_Re
set_Re
get_Im
set_Im
Add
Sub
Print

Як видно з результату, виводяться не тільки імена методів, але й імена методів зв’язаних з властивостями Re та Im класу. Щоб виділити окремо методи Add(), Sub(), Print() потрібно отримати перелік властивостей, потім в циклі сформувати нову множину методів без імен властивостей. Це вже інша тема.

 

5. Отримання інформації про тип оператором typeof(). Приклад

Отримати інформацію про тип можна з допомогою оператора typeof(), який має наступну загальну форму:

typeof(SomeType)

де SomeType – деякий тип.

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

class MyClass
{
  // ...
}

...

Type tp = typeof(MyClass);

У результаті отримується екземпляр tp типу System.Type, який містить відомості про клас MyClass.

Більш детально про використання оператора typeof() описується тут.

Приклад. На прикладі класу Complex (дивіться попередній приклад) отримується перелік конструкторів цього класу. Нижче наведено скорочений код розв’язку задачі.

using System;
using System.Reflection;

...

// Клас, що реалізує операції над комплексними числами
class Complex
{
  ...
}

...

static void Main(string[] args)
{

  // Отримати інформацію про типи класу Complex
  // з допомогою typeof()

  // 1. Отримати екземпляр типу System.Type з класу Complex
  Type tp = typeof(Complex);

  // 2. Отримати перелік конструкторів класу Complex.
  ConstructorInfo[] ci = tp.GetConstructors();

  // 4. Вивести перелік конструкторів з параметрами
  foreach (ConstructorInfo c in ci)
  {
    Console.Write(c.Name + "(");

    // Сформувати перелік параметрів конструктора
    ParameterInfo[] pi = c.GetParameters();

    for (int i = 0; i < pi.Length; i++)
    {
      Console.Write("{0} {1}",
        pi[i].ParameterType.Name, pi[i].Name);
      if (i < pi.Length - 1)
        Console.Write(", ");
    }
    Console.WriteLine(")");
  }

  Console.ReadKey();
}

...

Результат виконання програми

.ctor(Double _re, Double _im)
.ctor()

 

6. Отримання інформації про тип з допомогою методу System.Type.GetType(). Приклад

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

Метод GetType() має декілька перевантажених реалізацій, одна з яких має таку загальну форму

public static System.Type GetType(string typeName);

тут

  • typeName – ім’я типу, інформацію про який потрібно отримати. Зверніть увагу, що рядок представлений типом System.String.

Приклад. Нехай у програмі потрібно отримати інформацію про тип Triangle іншої збірки MyConsoleApp. На момент компіляції інформація про тип та збірку невідома. Відомі тільки ім’я збірки (MyConsoleApp) та ім’я типу (Triangle).
Нижченаведений фрагмент демонструє використання методу System.Type.GetType() для отримання інформації про тип Triangle. У фрагменті отримується перелік атрибутів класу Triangle.

using System;
using System.Reflection;

...

// 1. Завантажити збірку
Assembly asm = Assembly.Load("MyConsoleApp");

// 2. Отримати тип збірки
Type tp = asm.GetType("MyConsoleApp.Triangle");

// 3. Отримати атрибути класу MyClass2
object[] objs = tp.GetCustomAttributes(true);

// 4. Відобразити імена атрибутів на екрані
foreach (object obj in objs)
{
  Console.WriteLine(obj.GetType().Name);
}

...

 


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