355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Герберт Шилдт » C# 4.0: полное руководство » Текст книги (страница 42)
C# 4.0: полное руководство
  • Текст добавлен: 6 апреля 2017, 04:00

Текст книги "C# 4.0: полное руководство"


Автор книги: Герберт Шилдт



сообщить о нарушении

Текущая страница: 42 (всего у книги 83 страниц)

// Создать объект с помощью рефлексии.

using System;

using System.Reflection;

class MyClass {

  int x;

  int y;

  public MyClass(int i) {

    Console.WriteLine("Конструирование класса MyClass(int, int). ");

    x = y = i;

  }

  public MyClass(int i, int j) {

    Console.WriteLine("Конструирование класса MyClass(int, int). ");

    x = i;

    y = j;

    Show();

  }

  public int Sum() {

    return x + y;

  }

  public bool IsBetween(int i) {

    if ((x < i) && (i < y)) return true;

    else return false;

  }

  public void Set(int a, int b) {

    Console.Write("В методе Set (int, int). ");

    x = a;

    y = b;

    Show();

  }

  // Перегрузить метод Set.

  public void Set(double a, double b) {

    Console.Write("В методе(double, double). ");

    x = (int)a;

    y = (int)b;

    Show();

  }

  public void Show() {

    Console.WriteLine(«Значение x: {0}, значение у: {1}», x, y);

  }

}

class InvokeConsDemo {

  static void Main() {

    Type t = typeof(MyClass);

    int val;

    // Получить сведения о конструкторе.

    ConstructorInfo[] ci = t.GetConstructors();

    Console.WriteLine("Доступные конструкторы: ");

    foreach (ConstructorInfo с in ci) {

      // Вывести возвращаемый тип и имя.

      Console.Write(" " + t.Name + "(");

      // Вывести параметры.

      ParameterInfo[] pi = с.GetParameters();

      for (int i = 0; i < pi.Length; i++) {

        Console.Write(pi[i].ParameterType.Name + " " + pi[i].Name);

        if (i + 1 < pi.Length) Console.Write(", ");

      }

      Console.WriteLine(")");

    }

    Console.WriteLine();

    // Найти подходящий конструктор,

    int x;

    for (x = 0; x < ci.Length; x++) {

      ParameterInfo[] pi = ci[x].GetParameters();

      if (pi.Length == 2) break;

    }

    if (x == ci.Length) {

      Console.WriteLine(«Подходящий конструктор не найден.»);

      return;

    }

    else

      Console.WriteLine(«Найден конструктор с двумя параметрами.n»);

    // Сконструировать объект,

    object[] consargs = new object[2];

    consargs[0] = 10;

    consargs[1] = 20;

    object reflectOb = ci[x].Invoke(consargs);

    Console.WriteLine(«nВызов методов для объекта reflectOb.»);

    Console.WriteLine();

    MethodInfo[] mi = t.GetMethods();

    // Вызвать каждый метод,

    foreach (MethodInfo m in mi) {

      // Получить параметры.

      ParameterInfo[] pi = m.GetParameters();

      if (m.Name.CompareTo(«Set») == 0 &&

                    pi[0].ParameterType == typeof(int)) {

        // Это метод Set(int, int).

        object[] args = new object[2];

        args[0] = 9;

        args[1] = 18;

        m.Invoke(reflectOb, args);

      }

      else if (m.Name.CompareTo(«Set») == 0 &&

                    pi[0].ParameterType == typeof(double)) {

        // Это метод Set(double, double).

        object[] args = new object[2];

        args[0] = 1.12;

        args[1] = 23.4;

        m.Invoke(reflectOb, args);

      }

      else if (m.Name.CompareTo(«Sum») == 0) {

        val = (int)m.Invoke(reflectOb, null);

        Console.WriteLine("Сумма равна " + val);

      }

      else if (m.Name.CompareTo(«IsBetween») == 0) {

        object[] args = new object[1];

        args[0] = 14;

        if ((bool)m.Invoke(reflectOb, args))

          Console.WriteLine(«Значение 14 находится между x и у»);

      }

      else if (m.Name.CompareTo(«Show») == 0) {

        m.Invoke(reflectOb, null);

      }

    }

  }

}

Эта программа дает следующий результат.

Доступные конструкторы:

MyClass(Int32 i)

MyClass(Int32 i, Int32 j)

Найден конструктор с двумя параметрами.

Конструирование класса MyClass(int, int)

Значение х: 10, значение у: 20

Вызов методов для объекта reflectOb

Сумма равна 30

Значение 14 находится между х и у

В методе Set(int, int). Значение х: 9, значение у: 18

В методе Set(double, double). Значение х: 1, значение у: 23

Значение х: 1, значение у: 23

А теперь рассмотрим порядок применения рефлексии для конструирования объекта класса MyClass. Сначала получается перечень открытых конструкторов в следующей строке кода.

ConstructorInfo[] ci = t.GetConstructors();

Затем для наглядности примера выводятся полученные конструкторы. После этого осуществляется поиск по списку конструктора, принимающего два аргумента, как показано в приведенном ниже фрагменте кода.

for(x=0; х < ci.Length; х++)    {

  ParameterInfo[] pi = ci[x].GetParameters();

  if(pi.Length == 2) break;

}

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

// Сконструировать объект,

object[] consargs = new object[2];

consargs[0] = 10;

consargs[1] = 20;

object reflectOb = ci[x].Invoke(consargs);

После вызова метода Invoke() переменная экземпляра reflectOb будет ссылаться на объект типа MyClass. А далее в программе выполняются соответствующие методы для экземпляра этого объекта.

Следует, однако, иметь в виду, что ради простоты в данном примере предполагается наличие лишь одного конструктора с двумя аргументами типа int. Очевидно, что в реальном коде придется дополнительно проверять соответствие типов каждого параметра и аргумента.


Получение типов данных из сборок

В предыдущем примере все сведения о классе MyClass были получены с помощью рефлексии, за исключением одного элемента: типа самого класса MyClass. Несмотря на то что сведения о классе получались в предыдущем примере динамически, этот пример опирался на тот факт, что имя типа MyClass было известно заранее и использовалось в операторе typeof для получения объекта класса Туре, по отношению к которому осуществлялось косвенное или непосредственное обращение к методам рефлексии. В некоторых случаях такой подход может оказаться вполне пригодным, но истинные преимущества рефлексии проявляются лишь тогда, когда доступные в программе типы данных определяются динамически в результате анализа содержимого других сборок.

Как следует из главы 16, сборка несет в себе сведения о типах классов, структур и прочих элементов данных, которые в ней содержатся. Прикладной интерфейс Reflection API позволяет загрузить сборку, извлечь сведения о ней и получить экземпляры объектов любых открыто доступных в ней типов. Используя этот механизм, программа может выявлять свою среду и использовать те функциональные возможности, которые могут оказаться доступными без явного их определения во время компиляции. Это очень эффективный и привлекательный принцип. Представьте себе, например, программу, которая выполняет роль "браузера типов", отображая типы данных, доступные в системе, или же инструментальное средство разработки, позволяющее визуально составлять программы из различных типов данных, поддерживаемых в системе. А поскольку все сведения о типах могут быть извлечены и проверены, то ограничений на применение рефлексии практически не существует.

Для получения сведений о сборке сначала необходимо создать объект класса Assembly. В классе Assembly открытый конструктор не определяется. Вместо этого объект класса Assembly получается в результате вызова одного из его методов. Так, для загрузки сборки по заданному ее имени служит метод LoadFrom(). Ниже приведена его соответствующая форма:

static Assembly LoadFrom(string файл_сборки)

где файл_ сборки-обозначает конкретное имя файла сборки.

Как только будет получен объект класса Assembly, появится возможность обнаружить определенные в нем типы данных, вызвав для него метод GetTypes() в приведенной ниже общей форме.

Туре [] GetTypes()

Этот метод возвращает массив типов, содержащихся в сборке.

Для того чтобы продемонстрировать порядок обнаружения типов в сборке, потребуются два исходных файла. Первый файл будет содержать ряд классов, обнаруживаемых в коде из второго файла. Создадим сначала файл MyClasses.cs, содержащий следующий код.

// Файл, содержащий три класса и носящий имя MyClasses.cs.

using System;

class MyClass {

  int x;

  int y;

  public MyClass(int i) {

    Console.WriteLine("Конструирование класса MyClass(int). ");

    x = y = i;

    Show();

  }

  public MyClass(int i, int j) {

    Console.WriteLine("Конструирование класса MyClass(int, int). ");

    x = i;

    y = j;

    Show();

  }

  public int Sum() {

    return x + y;

  }

  public bool IsBetween(int i) {

    if ((x < i) && (i < y)) return true;

    else return false;

  }

  public void Set(int a, int b) {

    Console.Write("В методе Set(int, int). ");

    x = a;

    y = b;

    Show();

  }

  // Перегрузить.метод Set.

  public void Set(double a, double b) {

    Console.Write("В методе Set(double, double). ");

    x = (int)a;

    y = (int)b;

    Show();

  }

  public void Show() {

    Console.WriteLine(«Значение x: {0}, значение у: {1}», x, y);

  }

}

class AnotherClass {

  string msg;

  public AnotherClass(string str) {

    msg = str;

  }

  public void Show() {

    Console.WriteLine(msg);

  }

}

class Demo {

  static void Main() {

    Console.WriteLine(«Это заполнитель.»);

  }

}

Этот файл содержит класс MyClass, неоднократно использовавшийся в предыдущих примерах. Кроме того, в файл добавлены второй класс AnotherClass и третий класс Demo. Следовательно, сборка, полученная из исходного кода, находящегося в этом исходном файле, будет содержать три класса. Затем этот файл компилируется, и из него формируется исполняемый файл MyClasses.ехе. Именно эта сборка и будет опрашиваться программно.

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

/* Обнаружить сборку, определить типы и создать объект с помощью рефлексии. */

using System;

using System.Reflection;

class ReflectAssemblyDemo {

  static void Main() {

    int val;

    // Загрузить сборку MyClasses.exe.

    Assembly asm = Assembly.LoadFrom(«MyClasses.exe»);

    // Обнаружить типы, содержащиеся в сборке MyClasses.exe.

    Type[] alltypes = asm.GetTypes();

    foreach (Type temp in alltypes)

      Console.WriteLine("Найдено: " + temp.Name);

    Console.WriteLine();

    // Использовать первый тип, в данном случае – класс MyClass.

    Type t = alltypes[0]; // использовать первый найденный класс

    Console.WriteLine("Использовано: " + t.Name);

    // Получить сведения о конструкторе.

    ConstructorInfo[] ci = t.GetConstructors();

    Console.WriteLine("Доступные конструкторы: ");

    foreach (ConstructorInfo с in ci) {

      // Вывести возвращаемый тип и имя.

      Console.Write(" " + t.Name + "(");

      // Вывести параметры.

      ParameterInfo[] pi = с.GetParameters();

      for (int i = 0; i < pi.Length; i++) {

        Console.Write(pi[i].ParameterType.Name + " " + pi[i].Name);

        if (i + 1 < pi.Length) Console.Write(", ");

      }

      Console.WriteLine(")");

    }

    Console.WriteLine();

    // Найти подходящий конструктор,

    int x;

    for (x = 0; x < ci.Length; x++) {

      ParameterInfo[] pi = ci[x].GetParameters();

      if (pi.Length == 2) break;

    }

    if (x == ci.Length) {

      Console.WriteLine(«Подходящий конструктор не найден.»);

      return;

    }

    else

      Console.WriteLine(«Найден конструктор с двумя параметрами.n»);

    // Сконструировать объект,

    object[] consargs = new object[2];

    consargs[0] = 10;

    consargs[1] = 20;

    object reflectOb = ci[x].Invoke(consargs);

    Console.WriteLine(«Вызов методов для объекта reflectOb.»); Console.WriteLine();

    MethodInfo[] mi = t.GetMethods();

    // Вызвать каждый метод,

    foreach (MethodInfo m in mi) {

      //• Получить параметры.

      ParameterInfo[] pi = m.GetParameters();

      if (m.Name.CompareTo(«Set») == 0 &&

            pi[0].ParameterType == typeof(int)) {

        // Это метод Set(int, int).

        object[] args = new object[2];

        args[0] = 9;

        args[1] = 18;

        m.Invoke(reflectOb, args);

      }

      else if (m.Name.CompareTo(«Set») == 0 &&

            pi[0].ParameterType == typeof(double)) {

        // Это метод Set(double, double).

        object[] args = new object[2];

        args[0] = 1.12;

        args[1] = 23.4;

        m.Invoke(reflectOb, args);

      }

      else if (m.Name.CompareTo(«Sum») == 0) {

        val = (int)m.Invoke(reflectOb, null);

        Console.WriteLine("Сумма равна " + val);

      }

      else if (m.Name.CompareTo(«IsBetween») == 0) {

        object[] args = new object[1];

        args[0] = 14;

        if ((bool)m.Invoke(reflectOb, args))

          Console.WriteLine(«Значение 14 находится между x и у»);

      }

      else if ( m.Name.CompareTo(«Show») == 0)    {

        m.Invoke(reflectOb, null);

      }

    }

  }

}

При выполнении этой программы получается следующий результат.

Найдено: MyClass

Найдено: AnotherClass

Найдено: Demo

Использовано: MyClass

Доступные конструкторы:

  MyClass(Int32 i)

  MyClass(Int32 i, Int32 j)

Найден конструктор с двумя параметрами.

Конструирование класса MyClass(int, int)

Значение х: 10, значение у: 20

Вызов методов для объекта reflectOb

Сумма равна 30

Значение 14 находится между х и у

В методе Set (int, int) . Значение х: 9, значение у: 18

В методе Set(double, double). Значение х: 1, значение у: 23

Значение х: 1, значение у: 2 3

Как следует из результата выполнения приведенной выше программы, обнаружены все три класса, содержащиеся в файле сборки МуСlasses.ехе. Первым среди них обнаружен класс MyClass, который затем был использован для получения экземпляра объекта и вызова соответствующих методов.

Отдельные типы обнаруживаются в сборке MyClasses.ехе с помощью приведенной ниже последовательности кода, находящегося в самом начале методачМаin().

// Загрузить сборку MyClasses.exe.

Assembly asm = Assembly.LoadFrom(«MyClasses.ехе») ;

// Обнаружить типы, содержащиеся в сборке MyClasses.exe.

Туре[] alltypes = asm.GetTypes();

foreach(Type temp in alltypes)

  Console.WriteLine("Найдено: " + temp.Name);

Этой последовательностью кода можно пользоваться всякий раз, когда требуется динамически загружать и опрашивать сборку.

Но сборка совсем не обязательно должна быть исполняемым файлом с расширением .ехе. Сборки могут быть также в файлах динамически компонуемых библиотек (DLL) с расширением .dll. Так, если скомпилировать исходный файл MyClasses.cs в следующей командной строке:

csc /t:library MyClasses.es

то в итоге получится файл MyClasses.dll. Преимущество размещения кода в библиотеке DLL заключается, в частности, в том, что в этом случае метод Main() в исходном коде не нужен, тогда как всем исполняемым файлам требуется определенная точка входа, с которой должно начинаться выполнение программы. Именно поэтому класс Demo содержит метод Main() в качестве такой точки входа. А для библиотеки DLL метод Main() не требуется. Если же класс MyClass нужно превратить в библиотеку DLL, то в вызов метода LoadFrom() придется внести следующее изменение.

Assembly asm = Assembly.LoadFrom(«MyClasses.dll»);


Полностью автоматизированное обнаружение типов

Прежде чем завершить рассмотрение рефлексии, обратимся к еще одному поучительному примеру. Несмотря на то что в программе из предыдущего примера класс MyClass был полноценно использован без явного указания на его имя в программе, этот пример все же опирается на предварительную осведомленность о содержимом класса MyClass. Так, в программе были заранее известны имена методов Set и Sum из этого класса. Но с помощью рефлексии можно воспользоваться типом данных, ничего не зная о нем заранее. С этой целью придется извлечь все сведения, необходимые для конструирования объекта и формирования вызовов соответствующих методов. Такой подход может оказаться пригодным, например, при создании инструментального средства визуального проектирования, поскольку он позволяет использовать типы данных, имеющиеся в системе.

Рассмотрим следующий пример, демонстрирующий полностью автоматизированное обнаружение типов. В этом примере сначала загружается сборка MyClasses.ехе, затем конструируется объект класса MyClass и далее вызываются все методы, объявленные в классе MyClass, причем о них ничего заранее неизвестно.

// Использовать класс MyClass, ничего не зная о нем заранее.

using System;

using System.Reflection;

class ReflectAssemblyDemo {

  static void Main() {

    int val;

    Assembly asm = Assembly.LoadFrom(«MyClasses.exe»);

    Type[] alltypes = asm.GetTypes();

    Type t = alltypes[0]; // использовать первый обнаруженный класс

    Console.WriteLine("Использовано: " + t.Name);

    ConstructorInfo[] ci = t.GetConstructors();

    // Использовать первый обнаруженный конструктор.

    ParameterInfo[] cpi = ci[0].GetParameters();

    object reflectOb;

    if (cpi.Length > 0) {

      object[] consargs = new object[cpi.Length];

      // Инициализировать аргументы,

      for (int n = 0; n < cpi.Length; n++) consargs[n] = 10 + n * 20;

      // Сконструировать объект.

      reflectOb = ci[0].Invoke(consargs);

    }

    else

      reflectOb = ci[0].Invoke(null);

    Console.WriteLine(«nВызов методов для объекта reflectOb.»);

    Console.WriteLine();

    // Игнорировать наследуемые методы.

    MethodInfo[] mi = t.GetMethods(BindingFlags.DeclaredOnly |

                  BindingFlags.Instance | BindingFlags.Public);

    // Вызвать каждый метод,

    foreach (MethodInfo m in mi) {

      Console.WriteLine("Вызов метода {0} ", m.Name);

      // Получить параметры.

      ParameterInfo[] pi = m.GetParameters();

      // Выполнить методы,

      switch (pi.Length) {

        case 0: // аргументы отсутствуют

          if (m.ReturnType == typeof(int)) {

            val = (int)m.Invoke(reflectOb, null);

            Console.WriteLine("Результат: " + val);

          }

          else if (m.ReturnType == typeof(void)) {

            m.Invoke(reflectOb, null);

          }

          break;

        case 1: // один аргумент

          if (pi[0].ParameterType == typeof(int)) {

            object[] args = new object[1];

            args[0] = 14;

            if ((bool)m.Invoke(reflectOb, args))

              Console.WriteLine(«Значение 14 находится между x и у»);

            else

              Console.WriteLine(«Значение 14 не находится между х и у»);

          }

          break;

        case 2: // два аргумента

          if ((pi[0].ParameterType == typeof(int)) &&

                    (pi[1].ParameterType == typeof(int))) {

            object[] args = new object[2];

            args[0] = 9;

            args[1] = 18;

            m.Invoke(reflectOb, args);

          }

          else if ((pi[0].ParameterType == typeof(double)) &&

                    (pi[1].ParameterType == typeof(double))) {

            object[] args = new object[2];

            args[0] = 1.12;

            args[1] = 23.4;

            m.Invoke(reflectOb, args);

          }

          break;

      }

      Console.WriteLine();

    }

  }

}

Эта программа дает следующий результат.

Использовано: MyClass

Конструирование класса MyClass(int).

Значение х: 10, значение у: 10

Вызов методов для объекта reflectOb.

Вызов метода Sum

Результат: 20

Вызов метода IsBetween

Значение 14 не находится между х и у

Вызов метода Set

В методе Set (int, int). Значение х: 9, значение у: 18

Вызов метода Set

В методе Set(double, double). Значение х: 1, значение у: 23

Вызов метода Show

Значение х: 1, значение у: 23

Эта программа работает довольно просто, но все же требует некоторых пояснений. Во-первых, получаются и используются только те методы, которые явно объявлены в классе MyClass. Для этой цели служит форма BindingFlags метода GetMethods(), чтобы воспрепятствовать вызову методов, наследуемых от объекта. И во-вторых, количество параметров и возвращаемый тип каждого метода получаются динамически, а затем определяются и проверяются в операторе switch. На основании этой информации формируется вызов каждого метода.


    Ваша оценка произведения:

Популярные книги за неделю