Tutorial: Crear y usar objetos dinámicos en Visual Basic

Los objetos dinámicos exponen miembros como propiedades y métodos en tiempo de ejecución, en lugar de en tiempo de compilación. Esto le permite crear objetos para trabajar con estructuras que no coinciden con un formato o tipo estático. Por ejemplo, puede usar un objeto dinámico para hacer referencia a Document Object Model (DOM) HTML, que puede contener cualquier combinación de atributos y elementos de marcado HTML válidos. Dado que cada documento HTML es único, los miembros de un documento HTML específico se determinan en tiempo de ejecución. Un método común para hacer referencia a un atributo de un elemento HTML consiste en pasar el nombre del atributo al método GetProperty del elemento. Para hacer referencia al atributo id del elemento HTML <div id="Div1">, primero debe obtener una referencia al elemento <div> y, después, usar divElement.GetProperty("id"). Si usa un objeto dinámico, puede hacer referencia al atributo id como divElement.id.

Los objetos dinámicos también proporcionan un acceso cómodo a lenguajes dinámicos como IronPython e IronRuby. Puede usar un objeto dinámico para hacer referencia a un script dinámico que se interpreta en tiempo de ejecución.

Para hacer referencia a un objeto dinámico, use un enlace en tiempo de ejecución. El tipo de un objeto enlazado en tiempo de ejecución se especifica como Object. Para obtener más información, consulte Enlace en tiempo de compilación y en tiempo de ejecución.

Puede crear objetos dinámicos personalizados con las clases del espacio de nombres System.Dynamic. Por ejemplo, puede crear un objeto ExpandoObject y especificar los miembros de ese objeto en tiempo de ejecución. También puede crear su propio tipo que hereda la clase DynamicObject. Después, puede invalidar los miembros de la clase DynamicObject para proporcionar funciones dinámicas en tiempo de ejecución.

Este artículo contiene dos tutoriales independientes:

  • Crear un objeto personalizado que expone dinámicamente el contenido de un archivo de texto como propiedades de un objeto.

  • Crear un proyecto que usa una biblioteca IronPython.

Puede elegir una de estas dos opciones, o las dos, y, si opta por las dos, el orden no importa.

Prerrequisitos

Nota:

Es posible que tu equipo muestre nombres o ubicaciones diferentes para algunos de los elementos de la interfaz de usuario de Visual Studio en las siguientes instrucciones. La edición de Visual Studio que se tenga y la configuración que se utilice determinan estos elementos. Para obtener más información, vea Personalizar el IDE.

Creación de un objeto dinámico personalizado

En el primer tutorial se define un objeto dinámico personalizado que busca en el contenido de un archivo de texto. Una propiedad dinámica especifica el texto que se va a buscar. Por ejemplo, si el código de llamada especifica dynamicFile.Sample, la clase dinámica devuelve una lista genérica de cadenas que contiene todas las líneas del archivo que comienzan con "Sample". La búsqueda no distingue entre mayúsculas y minúsculas. La clase dinámica también admite dos argumentos opcionales. El primer argumento es un valor de enumeración de opción de búsqueda que especifica que la clase dinámica debe buscar coincidencias al principio de la línea, al final de la línea o en cualquier parte de la línea. El segundo argumento especifica que la clase dinámica debe recortar los espacios iniciales y finales de cada línea antes de buscar. Por ejemplo, si el código de llamada especifica dynamicFile.Sample(StringSearchOption.Contains), la clase dinámica busca "Sample" en cualquier parte de una línea. Si el código de llamada especifica dynamicFile.Sample(StringSearchOption.StartsWith, false), la clase dinámica busca "Sample" al principio de cada línea y no quita los espacios iniciales y finales. El comportamiento predeterminado de la clase dinámica es buscar una coincidencia al principio de cada línea y quitar los espacios iniciales y finales.

Para crear una clase dinámica personalizada

  1. Inicie Visual Studio.

  2. Seleccione Crear un proyecto.

  3. En el cuadro de diálogo Crear un proyecto, seleccione Visual Basic, después Aplicación de consola y luego Siguiente.

  4. En el cuadro de diálogo Configurar el nuevo proyecto, escriba DynamicSample como Nombre del proyecto y, después, seleccione Siguiente.

  5. En el cuadro de diálogo Información adicional, seleccione .NET 5.0 (actual) para Plataforma de destino y después Crear.

    El proyecto nuevo se crea.

  6. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto DynamicSample y seleccione Agregar>Clase. En el cuadro Nombre, escriba ReadOnlyFile y, después, seleccione Agregar.

    Se agrega un nuevo archivo que contiene la clase ReadOnlyFile.

  7. En la parte superior del archivo ReadOnlyFile.cs o ReadOnlyFile.vb, agregue el código siguiente para importar los espacios de nombres System.IO y System.Dynamic.

    Imports System.IO
    Imports System.Dynamic
    
  8. El objeto dinámico personalizado usa una enumeración para determinar los criterios de búsqueda. Antes de la instrucción de clase, agregue la siguiente definición de enumeración.

    Public Enum StringSearchOption
        StartsWith
        Contains
        EndsWith
    End Enum
    
  9. Actualice la instrucción de clase para heredar la clase DynamicObject, como se muestra en el ejemplo de código siguiente.

    Public Class ReadOnlyFile
        Inherits DynamicObject
    
  10. Agregue el código siguiente a la clase ReadOnlyFile para definir un campo privado para la ruta de acceso y un constructor para la clase 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. Agregue el método GetPropertyValue siguiente a la clase ReadOnlyFile. El método GetPropertyValue toma como entrada criterios de búsqueda y devuelve las líneas de un archivo de texto que coinciden con los criterios de búsqueda. Los métodos dinámicos proporcionados por la clase ReadOnlyFile llaman al método GetPropertyValue para recuperar los resultados correspondientes.

    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. Después del método GetPropertyValue, agregue el código siguiente para invalidar el método TryGetMember de la clase DynamicObject. Se llama al método TryGetMember cuando se solicita un miembro de una clase dinámica y no se especifican argumentos. El argumento binder contiene información sobre el miembro al que se hace referencia y el argumento result hace referencia al resultado devuelto para el miembro especificado. El método TryGetMember devuelve un valor booleano que devuelve true si el miembro solicitado existe. En caso contrario, devuelve 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. Después del método TryGetMember, agregue el código siguiente para invalidar el método TryInvokeMember de la clase DynamicObject. Se llama al método TryInvokeMember cuando se solicita un miembro de una clase dinámica con argumentos. El argumento binder contiene información sobre el miembro al que se hace referencia y el argumento result hace referencia al resultado devuelto para el miembro especificado. El argumento args contiene una matriz de los argumentos que se pasan al miembro. El método TryInvokeMember devuelve un valor booleano que devuelve true si el miembro solicitado existe. En caso contrario, devuelve false.

    La versión personalizada del método TryInvokeMember espera que el primer argumento sea un valor del enumerador StringSearchOption que se ha definido en un paso anterior. El método TryInvokeMember espera que el segundo argumento sea un valor booleano. Si uno o ambos argumentos son valores válidos, se pasan al método GetPropertyValue para recuperar los resultados.

    ' 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. Guarde y cierre el archivo.

Para crear un archivo de texto de ejemplo

  1. En el Explorador de soluciones, haga clic con el botón derecho en el proyecto DynamicSample y seleccione Agregar>Nuevo elemento. En el panel Plantillas instaladas seleccione General y, luego, la plantilla Archivo de texto. Deje el nombre predeterminado TextFile1.txt en el cuadro Nombre y después haga clic en Agregar. Se agregará un archivo de texto nuevo al proyecto.

  2. Copie el texto siguiente en el archivo 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. Guarde y cierre el archivo.

Para crear una aplicación de ejemplo que usa el objeto dinámico personalizado

  1. En el Explorador de soluciones, haga doble clic en el archivo Program.vb.

  2. Agregue el código siguiente al procedimiento Main para crear una instancia de la clase ReadOnlyFile para el archivo TextFile1.txt. El código usa el enlace en tiempo de ejecución para llamar a miembros dinámicos y recuperar líneas de texto que contienen la cadena "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. Guarde el archivo y presione Ctrl+F5 para compilar y ejecutar la aplicación.

Llamada a una biblioteca de lenguaje dinámico

En el siguiente tutorial se crea un proyecto que accede a una biblioteca escrita en el lenguaje dinámico IronPython.

Para crear una clase dinámica personalizada

  1. Abra Visual Studio, seleccione Archivo>Nuevo>Proyecto.

  2. En el cuadro de diálogo Crear un proyecto, seleccione Visual Basic, después Aplicación de consola y luego Siguiente.

  3. En el cuadro de diálogo Configurar el nuevo proyecto, escriba DynamicIronPythonSample como Nombre del proyecto y, después, seleccione Siguiente.

  4. En el cuadro de diálogo Información adicional, seleccione .NET 5.0 (actual) para Plataforma de destino y después Crear.

    El proyecto nuevo se crea.

  5. Instale el paquete NuGet IronPython.

  6. Edite el archivo Program.vb.

  7. En la parte superior del archivo, agregue el código siguiente para importar los espacios de nombres Microsoft.Scripting.Hosting y IronPython.Hosting de las bibliotecas de IronPython, y el espacio de nombres System.Linq.

    Imports Microsoft.Scripting.Hosting
    Imports IronPython.Hosting
    Imports System.Linq
    
  8. En el método Main, agregue el código siguiente para crear un objeto Microsoft.Scripting.Hosting.ScriptRuntime que hospede las bibliotecas de IronPython. El objeto ScriptRuntime carga el módulo de biblioteca de 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. Una vez que el código haya cargado el módulo random.py, agregue el código siguiente para crear una matriz de enteros. La matriz se pasa al método shuffle del módulo random.py, que ordena aleatoriamente los valores de la matriz.

    ' 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. Guarde el archivo y presione Ctrl+F5 para compilar y ejecutar la aplicación.

Consulte también