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

Dynamische Objekte machen Member wie etwa Eigenschaften und Methoden zur Laufzeit anstatt zur Kompilierzeit verfügbar. Dadurch können Sie Objekte erstellen, mit denen Sie mit Strukturen arbeiten können, die keinem statischen Typ oder Format entsprechen. Sie können z.B. ein dynamisches Objekt verwenden, um auf das HTML-DOM (Document Object Model) zu verweisen, das eine Kombination aus gültigen HTML-Markupelementen und Attributen enthalten kann. Da jedes HTML-Dokument eindeutig ist, werden die Elemente für ein bestimmtes HTML-Dokument zur Laufzeit bestimmt. Eine gängige Methode zum Verweisen eines Attributes auf ein HTML-Element ist, den Namen des Attributs an die GetProperty-Methode des Elements weiterzugeben. Rufen Sie zum Verweisen des id-Attributs auf das HTML-Element <div id="Div1"> zuerst einen Verweis auf das <div>-Element ab, und verwenden Sie anschließend divElement.GetProperty("id"). 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 wie IronPython und IronRuby. Sie können ein dynamisches Objekt verwenden, um sich auf ein dynamisches Skript zu beziehen, das zur Laufzeit ausgeführt wird.

Sie verweisen auf ein dynamisches Objekt mithilfe der späten Bindung. Geben Sie in C# den Typ eines spät gebundenen Objekts als dynamic an. Geben Sie in Visual Basic den Typ eines spät gebundenen Objekts als Object an. Weitere Informationen finden Sie unter dynamic und Frühes und spätes Binden.

Sie können benutzerdefinierte dynamische Objekte erstellen, indem Sie die Klassen im <xref:System.Dynamic?displayProperty=fullName>-Namespace verwenden. Sie können z.B. ein <xref:System.Dynamic.ExpandoObject> erstellen und die Member dieses Objekts zur Laufzeit angeben. Sie können auch einen eigenen Typ erstellen, der die <xref:System.Dynamic.DynamicObject>-Klasse erbt. Sie können die Member dieser <xref:System.Dynamic.DynamicObject>-Klasse anschließend außer Kraft setzen, um dynamische Funktionen zur Laufzeit bereitzustellen.

Im Verlauf dieser exemplarischen Vorgehensweise führen Sie folgende Aufgaben aus:

  • Erstellen Sie ein benutzerdefiniertes Objekt, das die Inhalte einer Textdatei als Eigenschaften eines Objekts weitergibt.

  • Erstellen Sie ein Objekt, das die IronPython-Bibliothek verwendet.

Erforderliche Komponenten

Sie benötigen IronPython 2.6.1 für .NET 4.0 für diese exemplarische Vorgehensweise. Sie können IronPython 2.6.1 für .NET 4.0 von CodePlex herunterladen.

Hinweis

Auf Ihrem Computer werden möglicherweise andere Namen oder Speicherorte für die Benutzeroberflächenelemente von Visual Studio angezeigt als die in den folgenden Anweisungen aufgeführten. Diese Elemente sind von der jeweiligen Visual Studio-Version und den verwendeten Einstellungen abhängig. Weitere Informationen finden Sie unter Personalisieren der IDE.

Erstellen eines benutzerdefinierten dynamischen Objekts

Das erste Projekt, das Sie in dieser exemplarischen Vorgehensweise erstellen definiert ein benutzerdefiniertes dynamisches Objekt, das die Inhalte einer Textdatei durchsucht. Der Text, nach dem gesucht werden soll, wird vom Namen einer dynamischen Eigenschaft angegeben. Wenn aufrufender Code z.B. dynamicFile.Sample angibt, gibt die dynamische Klasse eine generische Liste von Zeichenfolgen zurück, die alle Zeilen aus der Datei enthält, die mit „Sample“ anfangen. Die Groß- und Kleinschreibung wird bei der Suche nicht berücksichtigt. Die dynamische Klasse unterstützt auch zwei optionale Argumente. Das erste Argument ist ein Enumerationswert einer Suchoption, der angibt, dass die dynamische Klasse am Beginn, am Ende oder an einer beliebigen Stelle der Zeile nach Übereinstimmungen suchen soll. Das zweite Argument gibt an, dass die dynamische Klasse führende und nachfolgende Leerzeichen vor dem Suchvorgang aus jeder Zeile entfernen soll. Wenn aufrufender Code z.B. dynamicFile.Sample(StringSearchOption.Contains) angibt, sucht die dynamische Klasse an einer beliebigen Stelle in der Zeile nach „Sample“. Wenn der aufrufende Code dynamicFile.Sample(StringSearchOption.StartsWith, false) angibt, sucht die dynamische Klasse am Beginn jeder Zeile nach „Sample“ und entfernt keine führenden oder nachfolgenden Leerzeichen. Standardmäßig sucht die dynamische Klasse nach Übereinstimmungen am Beginn jeder Zeile und entfernt führende oder nachfolgende 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 dann auf OK. Das neue Projekt wird erstellt.

  4. Klicken Sie mit der rechten Maustaste auf das Projekt „DynamicSample“ und halten Sie den Mauszeiger auf Hinzufügen. Klicken Sie anschließend auf Klasse. Geben Sie im Feld Name die Bezeichnung ReadOnlyFile ein, und klicken Sie dann auf OK. Eine neue Datei wird hinzugefügt, die die Klasse „ReadOnlyFile“ enthält.

  5. Fügen Sie am Anfang der Dateien „ReadOnlyFile.cs“ oder „ReadOnlyFile.vb“ den folgenden Code zum Importieren der <xref:System.IO?displayProperty=fullName>- und <xref:System.Dynamic?displayProperty=fullName>-Namespaces hinzu.

    using System.IO;
    using System.Dynamic;
    
    Imports System.IO
    Imports System.Dynamic
    
  6. Das benutzerdefinierte dynamische Objekt verwendet einen Enumerationswert, um die Suchkriterien zu bestimmen. Fügen Sie vor der class-Anweisung die folgende Enumerationsdefinition ein.

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

    class ReadOnlyFile : DynamicObject
    
    Public Class ReadOnlyFile
        Inherits DynamicObject
    
  8. Fügen Sie den folgenden Code zur ReadOnlyFile-Klasse 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 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;
    }
    
    ' 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
    
  9. Fügen Sie die folgende GetPropertyValue-Methode zu der ReadOnlyFile-Klasse hinzu. Die GetPropertyValue-Methode akzeptiert Suchkriterien als Eingabe und gibt die Zeilen aus einer Textdatei zurück, die den Suchkriterien entsprechen. Die von der ReadOnlyFile-Klasse bereitgestellten Methoden rufen die GetPropertyValue-Methode auf, um ihre entsprechenden Ergebnisse abzurufen.

    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;
    }
    
    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
    
  10. Fügen Sie nach der GetPropertyValue-Methode den folgenden Code hinzu, um die <xref:System.Dynamic.DynamicObject.TryGetMember%2A>-Methode der <xref:System.Dynamic.DynamicObject>-Klasse zu überschreiben. Die Methode <xref:System.Dynamic.DynamicObject.TryGetMember%2A> wird aufgerufen, wenn ein Member einer dynamischen Klasse angefordert wird und keine Argumente angegeben werden. Das Argument binder enthält Informationen über den Member, auf den verwiesen wurde. Das Argument result verweist auf das Ergebnis, das für den angegebenen Member zurückgegeben wird. Die Methode <xref:System.Dynamic.DynamicObject.TryGetMember%2A> 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 override bool TryGetMember(GetMemberBinder binder,
                                      out object result) 
    {
        result = GetPropertyValue(binder.Name);
        return result == null ? false : true;
    }
    
    ' 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
    
  11. Fügen Sie nach der TryGetMember-Methode den folgenden Code hinzu, um die <xref:System.Dynamic.DynamicObject.TryInvokeMember%2A>-Methode der <xref:System.Dynamic.DynamicObject>-Klasse zu überschreiben. Die <xref:System.Dynamic.DynamicObject.TryInvokeMember%2A>-Methode wird aufgerufen, wenn ein Mitglied einer dynamischen Klasse mit Argumenten angefordert wird. Das Argument binder enthält Informationen über den Member, auf den verwiesen wurde. Das Argument result verweist auf das Ergebnis, das für den angegebenen Member zurückgegeben wird. Das Argument args enthält ein Array von Argumenten, die an den Member weitergegeben werden. Die Methode <xref:System.Dynamic.DynamicObject.TryInvokeMember%2A> 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 Methode TryInvokeMember erwartet, dass es sich beim ersten Argument um einen Wert der StringSearchOption-Enumeration handelt, die Sie in einem vorherigen Schritt definiert haben. Die Methode TryInvokeMember erwartet, dass es sich beim zweiten Argument um einen booleschen Wert handelt. Wenn es sich bei einem oder beiden Argumenten um gültige Werte handelt, werden sie an die Methode GetPropertyValue zum Abrufen der Werte weitergegeben.

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

So erstellen Sie eine Beispieltextdatei

  1. Klicken Sie mit der rechten Maustaste auf das Projekt „DynamicSample“ und halten Sie den Mauszeiger auf Hinzufügen. Klicken Sie anschließend auf Neues Element. Wählen Sie im Bereich Installierte Vorlagen Allgemein aus, und wählen Sie anschließend die Vorlage Textdatei aus. Übernehmen Sie den Standardnamen der Datei „TextFile1.txt“ im Feld Name, und klicken Sie auf Hinzufügen. Eine neue Textdatei wird zum Projekt 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. (http://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 enthält

  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 den folgenden Code zur Main-Prozedur hinzu, um eine Instanz der Klasse ReadOnlyFile für die Datei „TextFile1.txt“ zu erstellen. Der Code verwendet spätes Binden, um dynamische Member aufzurufen und die Textzeilen abzurufen, die die Zeichenfolge „Customer“ (Kunde) enthalten.

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

Aufrufen einer Bibliothek in einer dynamischen 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, müssen Sie IronPython 2.6.1 für .NET 4.0 installieren. Sie können IronPython 2.6.1 für .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 die Bezeichnung DynamicIronPythonSample ein, und klicken Sie dann auf OK. Das neue Projekt wird erstellt.

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

  4. Navigieren Sie auf der Registerkarte Durchsuchen zum Ordner, in dem die IronPython-Bibliotheken installiert sind. Z.B. C:\Programme\IronPython 2.6 für .NET 4.0. Wählen Sie die Bibliotheken IronPython.dll, IronPython.Modules.dll, Microsoft.Scripting.dll und Microsoft.Dynamic.dll. 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 den folgenden Code zum Importieren der Microsoft.Scripting.Hosting- und IronPython.Hosting-Namespaces aus den IronPython-Bibliotheken ein.

    using Microsoft.Scripting.Hosting;
    using IronPython.Hosting;
    
    Imports Microsoft.Scripting.Hosting
    Imports IronPython.Hosting
    
  7. Fügen Sie in der Main-Methode den folgenden Code zum Erstellen eines neuen Microsoft.Scripting.Hosting.ScriptRuntime-Objekts zum Hosten der IronPython-Bibliotheken hinzu. Das ScriptRuntime-Objekt lädt das IronPython-Bibliotheksmodul „random.py“.

    // 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.");
    
    ' 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.")
    
  8. Fügen Sie nach dem Code zum Laden des Moduls „random.py“ den folgenden Code zum Erstellen eines Arrays von ganzen Zahlen hinzu. Das Array wird an die shuffle-Methode des Moduls „random.py“ übergeben, das die Werte im Array per Zufall sortiert.

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

Siehe auch

<xref:System.Dynamic?displayProperty=fullName>
<xref:System.Dynamic.DynamicObject?displayProperty=fullName>
Verwenden von dynamischen Typen
Frühes und spätes Binden
dynamic
Implementieren von dynamischen Schnittstellen (externer Blog)