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


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

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

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

Ссылка на динамический объект выполняется с помощью позднего связывания. Тип объекта с поздней привязкой указывается как Object. Дополнительные сведения см. в разделе [Ранняя и поздняя привязка.

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

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

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

  • Создание проекта, использующего библиотеку IronPython.

Вы можете выполнить одно из них или оба, в последнем случае порядок не имеет значения.

Необходимые компоненты

  • Visual Studio 2019 версии 16.9 или более поздней с установленной рабочей нагрузкой Разработка классических приложений .NET. Пакет SDK для .NET 5 устанавливается автоматически при выборе этой рабочей нагрузки.

Примечание.

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

  • Для второго пошагового руководства установите IronPython для .NET. Перейдите на страницу загрузки для получения последней версии.

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

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

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

  1. Запустите Visual Studio.

  2. Выберите Создать новый проект.

  3. В диалоговом окне "Создание проекта" выберите Visual Basic, выберите консольное приложение и нажмите кнопку "Далее".

  4. В диалоговом окне Настройка нового проекта введите значение DynamicSample для параметра Имя проекта и нажмите кнопку Далее.

  5. В диалоговом окне Дополнительные сведения выберите значение .NET 5.0 (текущая) для параметра Целевая платформа, а затем нажмите кнопку Создать.

    Создается новый проект.

  6. В обозревателе решений щелкните проект DynamicSample правой кнопкой мыши и выберите Добавить>Класс. В поле Имя введите ReadOnlyFile, а затем нажмите кнопку Добавить.

    Будет добавлен новый файл, содержащий класс ReadOnlyFile.

  7. В верхней части файла ReadOnlyFile.cs илиReadOnlyFile.vb добавьте следующий код для импорта пространств имен System.IO и System.Dynamic.

    Imports System.IO
    Imports System.Dynamic
    
  8. Пользовательский динамический объект использует перечисление для определения условия поиска. Перед оператором класса добавьте следующее определение перечисления.

    Public Enum StringSearchOption
        StartsWith
        Contains
        EndsWith
    End Enum
    
  9. Обновите оператор класса, чтобы он наследовал класс DynamicObject, как показано в следующем примере кода.

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

    ' Store the path to the file and the initial line count value.
    Private p_filePath As String
    
    ' Public constructor. Verify that file exists and store the path in 
    ' the private variable.
    Public Sub New(ByVal filePath As String)
        If Not File.Exists(filePath) Then
            Throw New Exception("File path does not exist.")
        End If
    
        p_filePath = filePath
    End Sub
    
  11. Добавьте приведенный ниже метод GetPropertyValue в класс ReadOnlyFile. Метод GetPropertyValue принимает в качестве входных данных условие поиска и возвращает строки текстового файла, соответствующие этому условию. Динамический метод, предоставленный классом ReadOnlyFile, вызывает метод GetPropertyValue для извлечения соответствующих результатов.

    Public Function GetPropertyValue(ByVal propertyName As String,
                                     Optional ByVal StringSearchOption As StringSearchOption = StringSearchOption.StartsWith,
                                     Optional ByVal trimSpaces As Boolean = True) As List(Of String)
    
        Dim sr As StreamReader = Nothing
        Dim results As New List(Of String)
        Dim line = ""
        Dim testLine = ""
    
        Try
            sr = New StreamReader(p_filePath)
    
            While Not sr.EndOfStream
                line = sr.ReadLine()
    
                ' Perform a case-insensitive search by using the specified search options.
                testLine = UCase(line)
                If trimSpaces Then testLine = Trim(testLine)
    
                Select Case StringSearchOption
                    Case StringSearchOption.StartsWith
                        If testLine.StartsWith(UCase(propertyName)) Then results.Add(line)
                    Case StringSearchOption.Contains
                        If testLine.Contains(UCase(propertyName)) Then results.Add(line)
                    Case StringSearchOption.EndsWith
                        If testLine.EndsWith(UCase(propertyName)) Then results.Add(line)
                End Select
            End While
        Catch
            ' Trap any exception that occurs in reading the file and return Nothing.
            results = Nothing
        Finally
            If sr IsNot Nothing Then sr.Close()
        End Try
    
        Return results
    End Function
    
  12. После метода GetPropertyValue добавьте следующий код, чтобы переопределить метод TryGetMember класса DynamicObject. Метод TryGetMember вызывается при запросе члена динамического класса без указания аргументов. Аргумент binder содержит сведения об элементе, на который дается ссылка, а аргумент result ссылается на результат, возвращенный для указанного элемента. Метод TryGetMember возвращает логическое значение true, если запрошенный элемент существует. В противном случае возвращается false.

    ' Implement the TryGetMember method of the DynamicObject class for dynamic member calls.
    Public Overrides Function TryGetMember(ByVal binder As GetMemberBinder,
                                           ByRef result As Object) As Boolean
        result = GetPropertyValue(binder.Name)
        Return If(result Is Nothing, False, True)
    End Function
    
  13. После метода TryGetMember добавьте следующий код, чтобы переопределить метод TryInvokeMember класса DynamicObject. Метод TryInvokeMember вызывается при запросе члена динамического класса с аргументами. Аргумент binder содержит сведения об элементе, на который дается ссылка, а аргумент result ссылается на результат, возвращенный для указанного элемента. Аргумент args содержит массив аргументов, передаваемых в элемент. Метод TryInvokeMember возвращает логическое значение true, если запрошенный элемент существует. В противном случае возвращается false.

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

    ' Implement the TryInvokeMember method of the DynamicObject class for 
    ' dynamic member calls that have arguments.
    Public Overrides Function TryInvokeMember(ByVal binder As InvokeMemberBinder,
                                              ByVal args() As Object,
                                              ByRef result As Object) As Boolean
    
        Dim StringSearchOption As StringSearchOption = StringSearchOption.StartsWith
        Dim trimSpaces = True
    
        Try
            If args.Length > 0 Then StringSearchOption = CType(args(0), StringSearchOption)
        Catch
            Throw New ArgumentException("StringSearchOption argument must be a StringSearchOption enum value.")
        End Try
    
        Try
            If args.Length > 1 Then trimSpaces = CType(args(1), Boolean)
        Catch
            Throw New ArgumentException("trimSpaces argument must be a Boolean value.")
        End Try
    
        result = GetPropertyValue(binder.Name, StringSearchOption, trimSpaces)
    
        Return If(result Is Nothing, False, True)
    End Function
    
  14. Сохранить и закрыть файл.

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

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

  2. Скопируйте в файл 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
    
  3. Сохранить и закрыть файл.

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

  1. В Обозреватель решений дважды щелкните файл Program.vb.

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

    Dim rFile As Object = New ReadOnlyFile("..\..\..\TextFile1.txt")
    For Each line In rFile.Customer
        Console.WriteLine(line)
    Next
    Console.WriteLine("----------------------------")
    For Each line In rFile.Customer(StringSearchOption.Contains, True)
        Console.WriteLine(line)
    Next
    
  3. Сохраните файл и нажмите клавиши CTRL+F5 для сборки и запуска приложения.

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

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

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

  1. В Visual Studio выберите Файл>Создать>Проект.

  2. В диалоговом окне "Создание проекта" выберите Visual Basic, выберите консольное приложение и нажмите кнопку "Далее".

  3. В диалоговом окне Настройка нового проекта введите значение DynamicIronPythonSample для параметра Имя проекта и нажмите кнопку Далее.

  4. В диалоговом окне Дополнительные сведения выберите значение .NET 5.0 (текущая) для параметра Целевая платформа, а затем нажмите кнопку Создать.

    Создается новый проект.

  5. Установите пакет NuGet IronPython.

  6. Измените файл Program.vb .

  7. В верхней части файла добавьте следующий код для импорта пространств имен Microsoft.Scripting.Hosting и IronPython.Hosting из библиотек IronPython и пространства имен System.Linq.

    Imports Microsoft.Scripting.Hosting
    Imports IronPython.Hosting
    Imports System.Linq
    
  8. В методе 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")
    Dim py = Python.CreateRuntime()
    Dim random As Object = py.UseFile("random.py")
    Console.WriteLine("random.py loaded.")
    
  9. После указания в коде необходимости загрузки модуля random.py добавьте следующий код, чтобы создать массив целых чисел. Массив передается методу shuffle модуля random.py, который произвольно сортирует значения в массиве.

    ' Initialize an enumerable set of integers.
    Dim items = Enumerable.Range(1, 7).ToArray()
    
    ' Randomly shuffle the array of integers by using IronPython.
    For i = 0 To 4
        random.shuffle(items)
        For Each item In items
            Console.WriteLine(item)
        Next
        Console.WriteLine("-------------------")
    Next
    
  10. Сохраните файл и нажмите клавиши CTRL+F5 для сборки и запуска приложения.

См. также