Поделиться через


Пошаговое руководство. Создание и использование динамических объектов в C#

Динамические объекты предоставляют такие элементы, как свойства и методы во время выполнения, а не во время компиляции. Динамические объекты позволяют создавать объекты для работы со структурами, которые не соответствуют статичным типам или формату. Например, динамический объект можно использовать для ссылки на объектную модель HTML-документа (DOM), которая может содержать любое сочетание допустимых элементов разметки HTML и атрибутов. Так как каждый HTML-документ является уникальным, элементы для определенного HTML-документа определяются во время выполнения. Распространенный метод ссылки на атрибут html-элемента — передать имя атрибута GetProperty методу элемента. Чтобы ссылаться на id атрибут HTML-элемента <div id="Div1">, сначала получите ссылку на элемент <div>, а затем используйте divElement.GetProperty("id"). При использовании динамического объекта можно ссылаться на id атрибут как divElement.id.

Динамические объекты также обеспечивают удобный доступ к динамическим языкам, таким как IronPython и IronRuby. Динамический объект можно использовать для ссылки на динамический скрипт, интерпретируемый во время выполнения.

Вы обращаетесь к динамическому объекту, используя позднее связывание. Тип объекта с поздней привязкой указывается как dynamic. Дополнительные сведения см. в типе dynamic.

Вы можете создавать пользовательские динамические объекты, используя классы в пространстве имен System.Dynamic. Например, можно создать ExpandoObject и указать члены этого объекта во время выполнения. Вы также можете создать собственный тип, наследующий DynamicObject класс. Затем можно переопределить члены DynamicObject класса, чтобы обеспечить динамическую функциональность во время выполнения.

В этой статье содержатся два независимых пошагового руководства.

  • Создайте пользовательский объект, динамически предоставляющий содержимое текстового файла в качестве свойств объекта.
  • Создайте проект, использующий библиотеку IronPython .

Предпосылки

Замечание

На компьютере могут отображаться различные имена или расположения для некоторых элементов пользовательского интерфейса Visual Studio в следующих инструкциях. Выпуск Visual Studio, который у вас есть, и параметры, которые вы используете, определяют эти элементы. Дополнительные сведения см. в разделе Персонализация интегрированной среды разработки.

Создание настраиваемого динамического объекта

В первом пошаговом руководстве определяется пользовательский динамический объект, который выполняет поиск содержимого текстового файла. Динамическое свойство определяет текст, который нужно искать. Например, если вызывающий код указывает dynamicFile.Sample, динамический класс возвращает обобщенный список строк, содержащий все строки из файла, которые начинаются с "Sample". Поиск не учитывает регистр. Динамический класс также поддерживает два необязательных аргумента. Первый аргумент — это значение перечисления параметра поиска, указывающее, что динамический класс должен искать совпадения в начале строки, конце строки или в любом месте строки. Второй аргумент указывает, что динамический класс должен обрезать начальные и конечные пробелы от каждой строки перед поиском. Например, если вызывающий код задает dynamicFile.Sample(StringSearchOption.Contains), динамический класс ищет "Sample" в любом месте строки. Если вызывающий код указывает dynamicFile.Sample(StringSearchOption.StartsWith, false), динамический класс выполняет поиск "Sample" в начале каждой строки и не удаляет начальные и конечные пробелы. Поведение динамического класса по умолчанию заключается в поиске совпадения в начале каждой строки и удалении начальных и конечных пространств.

Создание настраиваемого динамического класса

Запустите Visual Studio. Выберите Создать новый проект. В диалоговом окне "Создание проекта " выберите C#, выберите консольное приложение и нажмите кнопку "Далее". В диалоговом окне "Настройка нового проекта " введите DynamicSampleимя проекта и нажмите кнопку "Далее". В диалоговом окне "Дополнительные сведения" выберите .NET 7.0 (Current) для целевой платформы и нажмите кнопку "Создать". В обозревателе решений щелкните правой кнопкой мыши проект DynamicSample и выберите "Добавить>класс". В поле "Имя" введите ReadOnlyFileи нажмите кнопку "Добавить". В верхней части файла ReadOnlyFile.cs или ReadOnlyFile.vb добавьте следующий код для импорта System.IO и System.Dynamic пространств имен.

using System.IO;
using System.Dynamic;

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

public enum StringSearchOption
{
    StartsWith,
    Contains,
    EndsWith
}

Обновите определение класса, чтобы наследовать класс DynamicObject, как показано в примере кода ниже.

class ReadOnlyFile : DynamicObject

Добавьте следующий код в ReadOnlyFile класс, чтобы определить частное поле для пути к файлу и конструктор для ReadOnlyFile класса.

// Store the path to the file and the initial line count value.
private string p_filePath;

// Public constructor. Verify that file exists and store the path in
// the private variable.
public ReadOnlyFile(string filePath)
{
    if (!File.Exists(filePath))
    {
        throw new Exception("File path does not exist.");
    }

    p_filePath = filePath;
}
  1. Добавьте следующий GetPropertyValue метод в ReadOnlyFile класс. Метод GetPropertyValue принимает в качестве входных данных, критерии поиска и возвращает строки из текстового файла, соответствующего этим критериям поиска. Динамические методы, ReadOnlyFile предоставляемые классом, вызывают GetPropertyValue метод для получения соответствующих результатов.
public List<string> GetPropertyValue(string propertyName,
                                     StringSearchOption StringSearchOption = StringSearchOption.StartsWith,
                                     bool trimSpaces = true)
{
    StreamReader sr = null;
    List<string> results = new List<string>();
    string line = "";
    string testLine = "";

    try
    {
        sr = new StreamReader(p_filePath);

        while (!sr.EndOfStream)
        {
            line = sr.ReadLine();

            // Perform a case-insensitive search by using the specified search options.
            testLine = line.ToUpper();
            if (trimSpaces) { testLine = testLine.Trim(); }

            switch (StringSearchOption)
            {
                case StringSearchOption.StartsWith:
                    if (testLine.StartsWith(propertyName.ToUpper())) { results.Add(line); }
                    break;
                case StringSearchOption.Contains:
                    if (testLine.Contains(propertyName.ToUpper())) { results.Add(line); }
                    break;
                case StringSearchOption.EndsWith:
                    if (testLine.EndsWith(propertyName.ToUpper())) { results.Add(line); }
                    break;
            }
        }
    }
    catch
    {
        // Trap any exception that occurs in reading the file and return null.
        results = null;
    }
    finally
    {
        if (sr != null) {sr.Close();}
    }

    return results;
}

Добавьте следующий код после метода GetPropertyValue, чтобы переопределить метод TryGetMember класса DynamicObject. Метод TryGetMember вызывается при запросе члена динамического класса, и аргументы не указаны. Аргумент binder содержит сведения о указанном элементе, а result аргумент ссылается на результат, возвращаемый для указанного элемента. Метод TryGetMember возвращает логическое значение, возвращающее true , если запрошенный член существует; в противном случае возвращается false.

// Implement the TryGetMember method of the DynamicObject class for dynamic member calls.
public override bool TryGetMember(GetMemberBinder binder,
                                  out object result)
{
    result = GetPropertyValue(binder.Name);
    return result == null ? false : true;
}

Добавьте следующий код после метода TryGetMember, чтобы переопределить метод TryInvokeMember класса DynamicObject. Метод TryInvokeMember вызывается, когда член динамического класса запрашивается с аргументами. Аргумент binder содержит сведения о указанном элементе, а result аргумент ссылается на результат, возвращаемый для указанного элемента. Аргумент args содержит массив аргументов, передаваемых члену. Метод TryInvokeMember возвращает логическое значение, возвращающее true , если запрошенный член существует; в противном случае возвращается false.

Пользовательская версия метода ожидает, что первый аргумент будет значением из перечисления TryInvokeMember, который вы определили на предыдущем шаге. Метод TryInvokeMember ожидает, что второй аргумент будет булевым значением. Если один или оба аргумента являются допустимыми значениями, они передаются методу GetPropertyValue для получения результатов.

// Implement the TryInvokeMember method of the DynamicObject class for
// dynamic member calls that have arguments.
public override bool TryInvokeMember(InvokeMemberBinder binder,
                                     object[] args,
                                     out object result)
{
    StringSearchOption StringSearchOption = StringSearchOption.StartsWith;
    bool trimSpaces = true;

    try
    {
        if (args.Length > 0) { StringSearchOption = (StringSearchOption)args[0]; }
    }
    catch
    {
        throw new ArgumentException("StringSearchOption argument must be a StringSearchOption enum value.");
    }

    try
    {
        if (args.Length > 1) { trimSpaces = (bool)args[1]; }
    }
    catch
    {
        throw new ArgumentException("trimSpaces argument must be a Boolean value.");
    }

    result = GetPropertyValue(binder.Name, StringSearchOption, trimSpaces);

    return result == null ? false : true;
}

Сохраните и закройте файл.

Создание примера текстового файла

В обозревателе решений щелкните правой кнопкой мыши проект DynamicSample и выберите "Добавить>новый элемент". В области "Установленные шаблоны " выберите "Общие" и выберите шаблон "Текстовый файл ". Оставьте имя по умолчанию TextFile1.txt в поле "Имя ", а затем нажмите кнопку "Добавить". Скопируйте следующий текст в файл TextFile1.txt .

List of customers and suppliers

Supplier: Lucerne Publishing (https://www.lucernepublishing.com/)
Customer: Preston, Chris
Customer: Hines, Patrick
Customer: Cameron, Maria
Supplier: Graphic Design Institute (https://www.graphicdesigninstitute.com/)
Supplier: Fabrikam, Inc. (https://www.fabrikam.com/)
Customer: Seubert, Roxanne
Supplier: Proseware, Inc. (http://www.proseware.com/)
Customer: Adolphi, Stephan
Customer: Koch, Paul

Сохраните и закройте файл.

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

В обозревателе решений дважды щелкните файл Program.cs . Добавьте следующий код в Main процедуру для создания экземпляра ReadOnlyFile класса для файла TextFile1.txt . Код использует позднюю привязку для вызова динамических элементов и получения строк текста, содержащих строку Customer.

dynamic rFile = new ReadOnlyFile(@"..\..\..\TextFile1.txt");
foreach (string line in rFile.Customer)
{
    Console.WriteLine(line);
}
Console.WriteLine("----------------------------");
foreach (string line in rFile.Customer(StringSearchOption.Contains, true))
{
    Console.WriteLine(line);
}

Сохраните файл и нажмите клавиши CTRL+F5 для сборки и запуска приложения.

Вызов динамической языковой библиотеки

В следующем пошаговом руководстве создается проект, который обращается к библиотеке, написанной на динамическом языке IronPython.

Создание настраиваемого динамического класса

В Visual Studio выберите файл>нового>проекта. В диалоговом окне "Создание проекта " выберите C#, выберите консольное приложение и нажмите кнопку "Далее". В диалоговом окне "Настройка нового проекта " введите DynamicIronPythonSampleимя проекта и нажмите кнопку "Далее". В диалоговом окне "Дополнительные сведения" выберите .NET 7.0 (Current) для целевой платформы и нажмите кнопку "Создать". Установите пакет NuGet IronPython . Измените файл Program.cs . В верхней части файла добавьте следующий код, чтобы импортировать пространства имен Microsoft.Scripting.Hosting и IronPython.Hosting из библиотек IronPython, а также пространство имен System.Linq.

using System.Linq;
using Microsoft.Scripting.Hosting;
using IronPython.Hosting;

В методе Main добавьте следующий код, чтобы создать новый Microsoft.Scripting.Hosting.ScriptRuntime объект для размещения библиотек IronPython. Объект ScriptRuntime загружает модуль библиотеки IronPython random.py.

// Set the current directory to the IronPython libraries.
System.IO.Directory.SetCurrentDirectory(
   Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) +
   @"\IronPython 2.7\Lib");

// Create an instance of the random.py IronPython library.
Console.WriteLine("Loading random.py");
ScriptRuntime py = Python.CreateRuntime();
dynamic random = py.UseFile("random.py");
Console.WriteLine("random.py loaded.");

После загрузки модуля random.py добавьте следующий код, чтобы создать массив целых чисел. Массив передается методу shuffle модуля random.py, который случайно сортирует значения в массиве.

// Initialize an enumerable set of integers.
int[] items = Enumerable.Range(1, 7).ToArray();

// Randomly shuffle the array of integers by using IronPython.
for (int i = 0; i < 5; i++)
{
    random.shuffle(items);
    foreach (int item in items)
    {
        Console.WriteLine(item);
    }
    Console.WriteLine("-------------------");
}

Сохраните файл и нажмите клавиши CTRL+F5 для сборки и запуска приложения.

См. также