Exemplarische Vorgehensweise: Erstellen und Verwenden von dynamischen Objekten in C#

Dynamische Objekte machen Member wie etwa Eigenschaften und Methoden zur Laufzeit anstatt zur Kompilierzeit verfügbar. Dadurch können Sie Objekte erstellen, anhand derer 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. Sie geben den Typ eines spät gebundenen Objekts als dynamic an. Weitere Informationen finden Sie unter dynamisch.

Sie können benutzerdefinierte dynamische Objekte erstellen, indem Sie die Klassen im System.Dynamic-Namespace verwenden. 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. Sie können die Member dieser DynamicObject-Klasse anschließend außer Kraft setzen, um dynamische Funktionen zur Laufzeit bereitzustellen.

Dieser Artikel enthält zwei voneinander unabhängige exemplarische Vorgehensweisen:

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

Voraussetzungen

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.

  • Wenn Sie die zweite exemplarische Vorgehensweise absolvieren möchten, installieren Sie IronPython für .NET. Wechseln Sie zur Downloadseite, um die neueste Version zu erhalten.

Erstellen eines benutzerdefinierten dynamischen Objekts

In der ersten exemplarischen Vorgehensweise wird ein benutzerdefiniertes dynamisches Objekt definiert, mit dem der Inhalt einer Textdatei durchsucht wird. Mit einer dynamischen Eigenschaft wird der Text angegeben, nach dem gesucht werden soll. 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.

Erstellen einer benutzerdefinierten dynamischen Klasse

Starten Sie Visual Studio. Wählen Sie Neues Projekt erstellen aus. Wählen Sie im Dialogfeld Neues Projekt erstellen die Option „C#“ aus, wählen Sie Konsolenanwendung und anschließend Weiter. Geben Sie im Dialogfeld Neues Projekt konfigurieren für Projektname den Text DynamicSample ein, und klicken Sie dann auf Weiter. Wählen Sie im Dialogfeld Zusätzliche Informationen für Zielframework die Option .NET 7.0 (aktuell) aus, und wählen Sie dann Erstellen. Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt „DynamicSample“, und klicken Sie anschließend auf Hinzufügen>Klasse. Geben Sie im Feld Name den Text ReadOnlyFile ein, und klicken Sie dann auf Hinzufügen. Fügen Sie am Anfang der Datei ReadOnlyFile.cs oder ReadOnlyFile.vb den folgenden Code hinzu, um die Namespaces System.IO und System.Dynamic zu importieren.

using System.IO;
using System.Dynamic;

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
}

Aktualisieren Sie die class-Anweisung wie im folgenden Beispiel, um die DynamicObject-Klasse zu erben.

class ReadOnlyFile : DynamicObject

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;
}
  1. 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;
}

Fügen Sie nach der GetPropertyValue-Methode den folgenden Code hinzu, um die TryGetMember-Methode der DynamicObject-Klasse zu überschreiben. Die Methode TryGetMember 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 TryGetMember 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;
}

Fügen Sie nach der TryGetMember-Methode den folgenden Code hinzu, um die TryInvokeMember-Methode der DynamicObject-Klasse zu überschreiben. Die TryInvokeMember-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 TryInvokeMember 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;
}

Speichern und schließen Sie die Datei.

Erstellen einer Beispieltextdatei

Klicken Sie im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt „DynamicSample“, und klicken Sie anschließend auf Hinzufügen>Neues Element. Wählen Sie im Bereich Installierte VorlagenAllgemein aus, und wählen Sie anschließend die Vorlage Textdatei aus. Übernehmen Sie im Feld Name den Standardnamen der Datei TextFile1.txt, und wählen Sie dann Hinzufügen. Kopieren Sie den folgenden Text in die Datei 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

Speichern und schließen Sie die Datei.

Erstellen einer Beispielanwendung, die das benutzerdefinierte dynamische Objekt verwendet

Doppelklicken Sie in Projektmappen-Explorer auf die Datei Program.cs. Fügen Sie der Main-Prozedur den folgenden Code 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);
}

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

In der folgenden exemplarischen Vorgehensweise wird ein Projekt erstellt, das auf eine Bibliothek zugreift, die in der dynamischen Sprache IronPython geschrieben ist.

So erstellen Sie eine benutzerdefinierte dynamische Klasse

Klicken Sie in Visual Studio auf Datei>Neu>Projekt. Wählen Sie im Dialogfeld Neues Projekt erstellen die Option „C#“ aus, wählen Sie Konsolenanwendung und anschließend Weiter. Geben Sie im Dialogfeld Neues Projekt konfigurieren für Projektname den Text DynamicIronPythonSample ein, und klicken Sie dann auf Weiter. Wählen Sie im Dialogfeld Zusätzliche Informationen für Zielframework die Option .NET 7.0 (aktuell) aus, und wählen Sie dann Erstellen. Installieren Sie das NuGet-Paket IronPython. Bearbeiten Sie die Datei Program.cs. Fügen Sie am Anfang der Datei den folgenden Code hinzu, um die Namespaces Microsoft.Scripting.Hosting und IronPython.Hosting aus den IronPython-Bibliotheken zu importieren sowie den Namespace System.Linq.

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

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.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.");

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

Speichern Sie die Datei, und drücken Sie STRG+F5, um die Anwendung zu erstellen und auszuführen.

Siehe auch