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);
}

...

 


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