Exemplarische Vorgehensweise: Erstellen und Verwenden von dynamischen Objekten (C# und Visual Basic)

Dynamische Objekte stellen Member, z. B. Eigenschaften oder Methoden, anstatt zum Zeitpunkt der Kompilierung zur Laufzeit bereit. So können Objekte erstellt werden, die mit Strukturen funktionieren, die keine Entsprechung in einem statischen Typ oder Format finden. Sie können das HTML-Dokument-Objektmodell (DOM), das eine beliebige Kombination von gültigen HTML-Markup-Elementen und Attributen enthalten kann, mithilfe eines dynamischen Objekts mit Verweisen versehen. Da jedes HTML-Dokument eindeutig ist, werden die Member für ein bestimmtes HTML-Dokument zur Laufzeit bestimmt. Eine gängige Methode für das Hinzufügen eines Verweises zu einem Attribut eines HTML-Elements ist die Weitergabe des Attributnamens an die GetProperty-Methode des Elements. Rufen Sie zuerst einen Verweis auf das <div>-Element ab, und verwenden Sie dann divElement.GetProperty("id"), um auf das id-Attribut des HTML-Elements <div id="Div1"> zu verweisen. Wenn Sie ein dynamisches Objekt verwenden, können Sie auf das id-Attribut als divElement.id verweisen.

Dynamische Objekte bieten außerdem einfachen Zugriff auf dynamische Sprachen, z. B. IronPython und IronRuby. Sie können ein dynamisches Objekt verwenden, um auf ein dynamisches Skript zu verweisen, das zur Laufzeit interpretiert wird.

Sie versehen ein dynamisches Objekt mithilfe der Funktion zur späten Bindung mit einem Verweis. In C# geben Sie den Typ eines spät gebundenen Objekts als dynamic an. In Visual Basic geben Sie den Typ eines spät gebundenen Objekts als Object an. Weitere Informationen finden Sie unter dynamic (C#-Referenz) und Frühes und spätes Binden (Visual Basic).

Sie können benutzerdefinierte dynamische Objekte mit den Klassen im System.Dynamic-Namespace erstellen. Sie können z. B. ein ExpandoObject erstellen und die Member dieses Objekts zur Laufzeit angeben. Sie können auch einen eigenen Typ erstellen, der die DynamicObject-Klasse erbt. Anschließend können Sie die Member der DynamicObject-Klasse überschreiben, um laufzeitbezogene dynamische Funktionalität bereitzustellen.

Im Verlauf dieser exemplarischen Vorgehensweise erfahren Sie, wie folgende Aufgaben ausgeführt werden:

  • Erstellen eines benutzerdefinierten Objekts, das den Inhalt einer Textdatei als Eigenschaften eines Objekts dynamisch bereitstellt.

  • Erstellen Sie ein Projekt, in dem eine IronPython-Bibliothek verwendet wird.

Vorbereitungsmaßnahmen

Sie benötigen IronPython 2.6.1 for .NET 4.0, um diese exemplarische Vorgehensweise auszuführen. Sie können IronPython 2.6.1 for .NET 4.0 von CodePlex herunterladen.

Tipp

Ihr Computer zeigt möglicherweise für einige der Elemente der Visual Studio-Benutzeroberfläche in der folgenden Anleitung andere Namen oder Standorte an. Diese Elemente sind von der jeweiligen Visual Studio-Version und den verwendeten Einstellungen abhängig. Weitere Informationen finden Sie unter Visual Studio-Einstellungen.

Erstellen eines benutzerdefinierten dynamischen Objekts

Im ersten Projekt, das wir in dieser exemplarischen Vorgehensweise erstellen, wird ein benutzerdefiniertes dynamisches Objekt definiert, das den Inhalt einer Textdatei durchsucht. Der Suchtext wird durch die Bezeichnung einer dynamischen Eigenschaft angegeben. Wenn im Aufrufcode z. B. dynamicFile.Sample angegeben wird, gibt die dynamische Klasse eine generische Liste von Zeichenfolgen zurück, die alle Zeilen der Datei enthält, die mit "Sample" beginnen. Bei der Suche wird die Groß- und Kleinschreibung nicht berücksichtigt. Von der dynamischen Klasse werden auch zwei optionale Argumente unterstützt. Das erste Argument ist ein Suchoptionsenumerationswert, der angibt, dass die dynamische Klasse am Anfang, am Ende oder an einer beliebigen Position in der Zeile nach Übereinstimmungen suchen soll. Das zweite Argument gibt an, dass die dynamische Klasse vor dem Suchen vorangestellte und nachgestellte Leerzeichen in jeder Zeile abschneiden soll. Wenn im Aufrufode z. B. dynamicFile.Sample(StringSearchOption.Contains) angegeben wird, wird von der dynamischen Klasse an einer beliebigen Zeilenposition nach "Sample" gesucht. Wird im Aufrufcode dynamicFile.Sample(StringSearchOption.StartsWith, false) angegeben, sucht die dynamische Klasse am Anfang jeder Zeile nach "Sample" und entfernt keine vorangestellten und nachgestellten Leerzeichen. Standardmäßig sucht die dynamische Klasse am Zeilenanfang nach einer Übereinstimmung und entfernt vorangestellte und nachgestellte Leerzeichen.

So erstellen Sie eine benutzerdefinierte dynamische Klasse

  1. Starten Sie Visual Studio.

  2. Zeigen Sie im Menü Datei auf Neu, und klicken Sie dann auf Projekt.

  3. Überprüfen Sie, ob im Dialogfeld Neues Projekt im Bereich Projekttypen der Eintrag Windows ausgewählt ist. Wählen Sie im Bereich Vorlagen die Option Konsolenanwendung aus. Geben Sie im Feld Name die Bezeichnung DynamicSample ein, und klicken Sie auf OK. Das neue Projekt wird erstellt.

  4. Klicken Sie mit der rechten Maustaste auf das Projekt "DynamicSample", zeigen Sie auf Hinzufügen, und klicken Sie dann auf Klasse. Geben Sie im Feld Name die Bezeichnung ReadOnlyFile ein, und klicken Sie auf OK. Eine neue Datei, die die ReadOnlyFile-Klasse enthält, wird hinzugefügt.

  5. Fügen Sie am Anfang von "ReadOnlyFile.cs" oder "ReadOnlyFile.vb" den folgenden Code hinzu, um den System.IO-Namespace und den System.Dynamic-Namespace zu importieren.

    Imports System.IO
    Imports System.Dynamic
    
    using System.IO;
    using System.Dynamic;
    
  6. Die Suchkriterien werden vom benutzerdefinierten dynamischen Objekt mithilfe einer Enumeration bestimmt. Fügen Sie vor der Klassenanweisung die folgende Enumerationsdefinition hinzu.

    Public Enum StringSearchOption
        StartsWith
        Contains
        EndsWith
    End Enum
    
    public enum StringSearchOption
    {
        StartsWith,
        Contains,
        EndsWith
    }
    
  7. Aktualisieren Sie, wie im folgenden Codebeispiel gezeigt, die Klassenanweisung, um die DynamicObject-Klasse zu erben.

    Public Class ReadOnlyFile
        Inherits DynamicObject
    
    class ReadOnlyFile : DynamicObject
    
  8. Fügen Sie der ReadOnlyFile-Klasse den folgenden Code hinzu, um ein privates Feld für den Dateipfad und einen Konstruktor für die ReadOnlyFile-Klasse zu definieren.

    ' 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
    
    // 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;
    }
    
  9. Fügen Sie der ReadOnlyFile-Klasse die GetPropertyValue-Methode hinzu. Die GetPropertyValue-Methode akzeptiert Suchkriterien als Eingabe und gibt die Zeilen einer Textdatei zurück, die mit den Suchkriterien übereinstimmen. Die dynamischen, von der ReadOnlyFile-Klasse bereitgestellten Methoden rufen die GetPropertyValue-Methode auf, um die jeweiligen Ergebnisse abzurufen.

    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
    
    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;
    }
    
  10. Fügen Sie nach der GetPropertyValue-Methode den folgenden Code zum Überschreiben der TryGetMember-Methode der DynamicObject-Klasse hinzu. Die TryGetMember-Methode wird aufgerufen, wenn ein Member einer dynamischen Klasse angefordert wird und keine Argumente angegeben werden. Das binder-Argument enthält Informationen zum Member, auf den verwiesen wird, und das result-Argument verweist auf das für den angegebenen Member zurückgegebene Ergebnis. Die TryGetMember-Methode gibt einen booleschen Wert zurück, der true zurückgibt, wenn der angeforderte Member vorhanden ist. Andernfalls wird false zurückgegeben.

    ' 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
    
    // 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;
    }
    
  11. Fügen Sie nach der TryGetMember-Methode den folgenden Code zum Überschreiben der TryInvokeMember-Methode der DynamicObject-Klasse hinzu. Die TryInvokeMember-Methode wird aufgerufen, wenn ein Member einer dynamischen Klasse mit Argumenten angefordert wird. Das binder-Argument enthält Informationen zum Member, auf den verwiesen wird, und das result-Argument verweist auf das für den angegebenen Member zurückgegebene Ergebnis. Das args-Argument enthält ein Array der Argumente, die an den Member weitergegeben werden. Die TryInvokeMember-Methode gibt einen booleschen Wert zurück, der true zurückgibt, wenn der angeforderte Member vorhanden ist. Andernfalls wird false zurückgegeben.

    Die benutzerdefinierte Version der TryInvokeMember-Methode erwartet, dass das erste Argument ein Wert der StringSearchOption-Enumeration ist, der in einem der vorherigen Schritt definiert wurde. Die TryInvokeMember-Methode erwartet, dass das zweite Argument ein boolescher Wert ist. Wenn ein Argument bzw. beide Argumente gültige Werte sind, werden sie an die GetPropertyValue-Methode übergeben, um die Ergebnisse abzurufen.

    ' 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
    
    // 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;
    }
    
  12. Speichern und schließen Sie die Datei.

So erstellen Sie eine Beispieltextdatei

  1. Klicken Sie mit der rechten Maustaste auf das Projekt "DynamicSample", zeigen Sie auf Hinzufügen, und klicken Sie dann auf Neues Element. Wählen Sie im Bereich Installierte Vorlagen die Option Allgemein und anschließend die Vorlage Textdatei aus. Lassen Sie den Standardnamen von "TextFile1.txt" im Feld Name unverändert, und klicken Sie auf Hinzufügen. Dem Projekt wird die neue Textdatei hinzugefügt.

  2. Kopieren Sie den folgenden Text in die Datei "TextFile1.txt".

    List of customers and suppliers
    
    Supplier: Lucerne Publishing (http://www.lucernepublishing.com/)
    Customer: Preston, Chris
    Customer: Hines, Patrick
    Customer: Cameron, Maria
    Supplier: Graphic Design Institute (http://www.graphicdesigninstitute.com/) 
    Supplier: Fabrikam, Inc. (http://www.fabrikam.com/) 
    Customer: Seubert, Roxanne
    Supplier: Proseware, Inc. (https://www.proseware.com/) 
    Customer: Adolphi, Stephan
    Customer: Koch, Paul
    
  3. Speichern und schließen Sie die Datei.

So erstellen Sie eine Beispielanwendung, die das benutzerdefinierte dynamische Objekt verwendet

  1. Doppelklicken Sie im Projektmappen-Explorer auf die Datei "Module1.vb", wenn Sie Visual Basic verwenden, oder auf die Datei "Program.cs", wenn Sie Visual C# verwenden.

  2. Fügen Sie der Main-Prozedur den folgenden Code hinzu, um für die Datei "TextFile1.txt" eine Instanz der ReadOnlyFile-Klasse zu erstellen. Der Code verwendet späte Bindung, um dynamische Member aufzurufen und Textzeilen abzurufen, die die Zeichenfolge "Customer" enthalten.

    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
    
    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);
    }
    
  3. Speichern Sie die Datei, und drücken Sie STRG+F5, um die Anwendung zu erstellen und auszuführen.

Aufrufen einer Bibliothek für eine dynamische Sprache

Das nächste Projekt, das Sie in dieser exemplarischen Vorgehensweise erstellen, greift auf eine Bibliothek zu, die in der dynamischen Sprache IronPython geschrieben ist. Bevor Sie dieses Projekt erstellen, muss IronPython 2.6.1 for .NET 4.0. installiert sein. Sie können IronPython 2.6.1 for .NET 4.0 von CodePlex herunterladen.

So erstellen Sie eine benutzerdefinierte dynamische Klasse

  1. Zeigen Sie in Visual Studio im Menü Datei auf Neu, und klicken Sie dann auf Projekt.

  2. Überprüfen Sie, ob im Dialogfeld Neues Projekt im Bereich Projekttypen der Eintrag Windows ausgewählt ist. Wählen Sie im Bereich Vorlagen die Option Konsolenanwendung aus. Geben Sie im Feld Name den Namen DynamicIronPythonSample ein, und klicken Sie dann auf OK. Das neue Projekt wird erstellt.

  3. Wenn Sie Visual Basic verwenden, klicken Sie mit der rechten Maustaste auf das DynamicIronPythonSample-Projekt, und klicken Sie dann auf Eigenschaften. Klicken Sie auf die Registerkarte Verweise. Klicken Sie auf die Schaltfläche Hinzufügen. Wenn Sie Visual C#, verwenden, klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf den Ordner Verweise und dann auf Verweis hinzufügen.

  4. Navigieren Sie auf der Registerkarte Durchsuchen zu dem Ordner, in dem die IronPython-Bibliotheken installiert sind. Beispiel: C:\Programme\IronPython 2.6 for .NET 4.0. Wählen Sie die Bibliotheken IronPython.dll, IronPython.Modules.dll, Microsoft.Scripting.dll und Microsoft.Dynamic.dll aus. Klicken Sie auf OK.

  5. Wenn Sie Visual Basic verwenden, bearbeiten Sie die Datei Module1.vb. Wenn Sie Visual C# verwenden, bearbeiten Sie die Datei Program.cs.

  6. Fügen Sie am Anfang der Datei folgenden Code hinzu, um den IronPython.Hosting-Namespace und den Microsoft.Scripting.Hosting-Namespace aus den IronPython-Bibliotheken zu importieren.

    Imports Microsoft.Scripting.Hosting
    Imports IronPython.Hosting
    
    using Microsoft.Scripting.Hosting;
    using IronPython.Hosting;
    
  7. Fügen Sie in der Main-Methode folgenden Code hinzu, um ein neues Microsoft.Scripting.Hosting.ScriptRuntime-Objekt zum Hosten der IronPython-Bibliotheken zu erstellen. Das ScriptRuntime-Objekt lädt das IronPython-Bibliotheksmodul random.py.

    ' Set the current directory to the IronPython libraries.
    My.Computer.FileSystem.CurrentDirectory = 
       My.Computer.FileSystem.SpecialDirectories.ProgramFiles &
       "\IronPython 2.6 for .NET 4.0\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.")
    
    // Set the current directory to the IronPython libraries.
    System.IO.Directory.SetCurrentDirectory(
       Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) + 
       @"\IronPython 2.6 for .NET 4.0\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.");
    
  8. Fügen Sie nach dem Code zum Laden des Moduls random.py folgenden Code hinzu, um ein Array von ganzen Zahlen zu erstellen. Das Array wird an die shuffle-Methode des Moduls random.py übergeben, das die Werte im Array nach dem Zufallsprinzip sortiert.

    ' 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
    
    // 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("-------------------");
    }
    
  9. Speichern Sie die Datei, und drücken Sie STRG+F5, um die Anwendung zu erstellen und auszuführen.

Siehe auch

Referenz

System.Dynamic

System.Dynamic.DynamicObject

dynamic (C#-Referenz)

Konzepte

Frühes und spätes Binden (Visual Basic)

Weitere Ressourcen

Neue exemplarische Vorgehensweisen (C# und Visual Basic)