Compartir a través de


Tutorial: Crear y usar objetos dinámicos en C#

Los objetos dinámicos exponen miembros como propiedades y métodos en tiempo de ejecución, en lugar de en tiempo de compilación. Los objetos dinámicos permiten crear objetos para trabajar con estructuras que no coinciden con un tipo o formato estáticos. Por ejemplo, puede usar un objeto dinámico para hacer referencia al Modelo de objetos de documento HTML (DOM), 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 determinado se determinan en tiempo de ejecución. Un método común para hacer referencia a un atributo de un elemento HTML es pasar el nombre del atributo al GetProperty método del elemento. Para hacer referencia al id atributo del elemento <div id="Div1">HTML, primero se obtiene una referencia al <div> elemento y, a continuación, se usa divElement.GetProperty("id"). Si usa un objeto dinámico, puede hacer referencia al id atributo como divElement.id.

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

Se hace referencia a un objeto dinámico mediante la vinculación tardía. Especifique el tipo de un objeto enlazado en tiempo de ejecución como dynamic. Para obtener más información, consulte dynamic.

Puede crear objetos dinámicos personalizados mediante las clases del System.Dynamic espacio de nombres . Por ejemplo, puede crear un ExpandoObject y especificar los miembros de ese objeto en tiempo de ejecución. También puede crear su propio tipo que herede la DynamicObject clase . A continuación, puede sobrescribir los miembros de la clase DynamicObject para proporcionar funcionalidad dinámica en tiempo de ejecución.

Este artículo contiene dos tutoriales independientes:

  • Cree un objeto personalizado que exponga dinámicamente el contenido de un archivo de texto como propiedades de un objeto .
  • Cree un proyecto que use una IronPython biblioteca.

Prerrequisitos

Nota:

El equipo puede mostrar nombres o ubicaciones diferentes para algunos de los elementos de la interfaz de usuario de Visual Studio en las instrucciones siguientes. La edición de Visual Studio que tiene y la configuración que usa determinan estos elementos. Para obtener más información, consulte Personalizando el IDE.

Crear un objeto dinámico personalizado

En el primer tutorial se define un objeto dinámico personalizado que busca el contenido de un archivo de texto. Una propiedad dinámica especifica el texto que se va a buscar. Por ejemplo, si la llamada a código especifica dynamicFile.Sample, la clase dinámica devuelve una lista genérica de cadenas que contiene todas las líneas del archivo que comienzan por "Sample". La búsqueda no distingue mayúsculas de 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, el 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 la llamada a código especifica dynamicFile.Sample(StringSearchOption.Contains), la clase dinámica busca "Sample" en cualquier lugar 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 deja intactos los espacios al principio y al final. 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.

Creación de una clase dinámica personalizada

Inicie Visual Studio. Seleccione Crear un nuevo proyecto. En el cuadro de diálogo Crear un nuevo proyecto , seleccione C#, aplicación de consola y, después, siguiente. En el cuadro de diálogo Configurar el nuevo proyecto , escriba DynamicSample en Nombre del proyecto y, a continuación, seleccione Siguiente. En el cuadro de diálogo Información adicional , seleccione .NET 7.0 (Actual) para la plataforma de destino y, a continuación, seleccione Crear. 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 ReadOnlyFiley, a continuación, seleccione Agregar. En la parte superior del archivo ReadOnlyFile.cs o ReadOnlyFile.vb, agregue el siguiente código para importar los espacios de nombres System.IO y System.Dynamic.

using System.IO;
using System.Dynamic;

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
}

Actualice la instrucción de clase para heredar la DynamicObject clase, como se muestra en el ejemplo de código siguiente.

class ReadOnlyFile : DynamicObject

Agregue el código siguiente a la ReadOnlyFile clase para definir un campo privado para la ruta de acceso del archivo y un constructor para la ReadOnlyFile clase .

// 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. Agregue el método siguiente GetPropertyValue a la ReadOnlyFile clase . El GetPropertyValue método toma, como entrada, criterios de búsqueda y devuelve las líneas de un archivo de texto que coinciden con esos criterios de búsqueda. Los métodos dinámicos proporcionados por la ReadOnlyFile clase llaman al GetPropertyValue método para recuperar sus resultados respectivos.
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;
}

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

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

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

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

Guarde y cierre el archivo.

Creación de un archivo de texto de ejemplo

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, a continuación, seleccione la plantilla Archivo de texto . Deje el nombre predeterminado de TextFile1.txt en el cuadro Nombre y, a continuación, seleccione Agregar. 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

Guarde y cierre el archivo.

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

En el Explorador de soluciones, haga doble clic en el archivo Program.cs . Agregue el código siguiente al Main procedimiento para crear una instancia de la ReadOnlyFile clase para el archivo TextFile1.txt . El código usa la vinculación tardía para llamar a miembros dinámicos y recuperar líneas de texto que contienen la cadena "Cliente".

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

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

Abra Visual Studio, seleccione Archivo>Nuevo>Proyecto. En el cuadro de diálogo Crear un nuevo proyecto , seleccione C#, aplicación de consola y, después, siguiente. En el cuadro de diálogo Configurar el nuevo proyecto , escriba DynamicIronPythonSample en Nombre del proyecto y, a continuación, seleccione Siguiente. En el cuadro de diálogo Información adicional , seleccione .NET 7.0 (Actual) para la plataforma de destino y, a continuación, seleccione Crear. Instale el paquete NuGet IronPython . Edite el archivo Program.cs . 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, así como el espacio de nombres System.Linq.

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

En el método Main, agregue el código siguiente para crear un nuevo Microsoft.Scripting.Hosting.ScriptRuntime objeto para hospedar las bibliotecas de IronPython. El ScriptRuntime objeto carga el módulo de biblioteca 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.");

Después del código para cargar el módulo random.py, agregue el código siguiente para crear una matriz de enteros. La matriz se pasa al shuffle método del módulo random.py, que ordena aleatoriamente los valores de la matriz.

// 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("-------------------");
}

Guarde el archivo y presione Ctrl+F5 para compilar y ejecutar la aplicación.

Consulte también