Schreiben von Code zum Anpassen einer domänenspezifischen Sprache
In diesem Abschnitt wird gezeigt, wie Sie mithilfe von benutzerdefiniertem Code ein Modell in einer domänenspezifischen Sprache (DSL) erstellen, es ändern oder darauf zugreifen.
Kontext zum Schreiben von Code für eine DSL
Es gibt mehrere Kontexte, in denen Sie Code schreiben können, der mit einer DSL funktioniert:
Benutzerdefinierte Befehle Sie können einen Befehl erstellen, den die Benutzer per Rechtsklick auf das Diagramm aufrufen, und mit dem das Modell geändert werden kann. Weitere Informationen finden Sie unter Gewusst wie: Hinzufügen eines Befehls zum Kontextmenü.
Validierung. Sie können Code schreiben, der überprüft, ob sich das Modell in einem korrekten Zustand befindet. Weitere Informationen finden Sie unter Validierung in einer domänenspezifischen Sprache.
Ändern des Standardverhaltens: Sie können zahlreiche Aspekte des Codes ändern, der über „DslDefinition.dsl“ generiert wird. Weitere Informationen finden Sie unter Überschreiben und Erweitern der generierten Klassen.
Texttransformation: Sie können Textvorlagen schreiben, bei denen der enthaltene Code auf ein Modell zugreift und eine Textdatei generiert, z. B. um Programmcode zu erzeugen. Weitere Informationen finden Sie unter Generieren von Code für eine domänenspezifische Sprache.
Andere Visual Studio-Erweiterungen: Sie können separate VSIX-Erweiterungen schreiben, die Modelle lesen und ändern. Weitere Informationen finden Sie unter Gewusst wie: Öffnen eines Modells über eine Datei im Programmcode.
In-Memory-Speicher
Instanzen der Klassen, die Sie in DslDefinition.dsl definieren, werden in einer Datenstruktur abgelegt, die als In-Memory-Speicher (IMS) oder Speicher bezeichnet wird. Die in einer DSL definierten Klassen verwenden immer einen Speicher als Argument für den Konstruktor. Wenn Ihre DSL beispielsweise eine Klasse mit dem Namen Example
definiert:
Example element = new Example (theStore);
Aufbewahrung der Objekte im Speicher bietet gegenüber gewöhnlichen Objekten mehrere Vorteile.
Transaktionen: Sie können eine Reihe von zusammenhängenden Änderungen in einer Transaktion zusammenfassen:
using (Transaction t = store.TransactionManager.BeginTransaction("updates")) { // make several changes to Store elements here t.Commit(); }
Wenn es während der Änderungen zu einer Ausnahme kommt, sodass der abschließende
Commit()
nicht ausgeführt wird, wird der Speicher auf seinen vorherigen Zustand zurückgesetzt. Mit diesem Ansatz können Sie sicherstellen, dass das Modell nicht durch Fehler in einen inkonsistenten Zustand versetzt wird. Weitere Informationen finden Sie unter Navigieren in und Aktualisieren von Modellen im Programmcode.Binäre Beziehungen: Wenn Sie eine Beziehung zwischen zwei Klassen definieren, verfügen die Instanzen an beiden Endpunkten über eine Eigenschaft, die auf den jeweils anderen Endpunkt verweist. Die beiden Endpunkte werden stets synchronisiert. Wenn Sie beispielsweise eine Elternschaftsbeziehung mit den Rollen Parents und Children definieren, könnten Sie Folgendes schreiben:
John.Children.Add(Mary)
Beide der folgenden Ausdrücke sind jetzt wahr:
John.Children.Contains(Mary)
Mary.Parents.Contains(John)
Sie können den gleichen Effekt erzielen, indem Sie Folgendes schreiben:
Mary.Parents.Add(John)
Weitere Informationen finden Sie unter Navigieren in und Aktualisieren von Modellen im Programmcode.
Regeln und Ereignisse: Sie können Regeln definieren, die immer dann ausgelöst werden, wenn angegebene Änderungen vorgenommen werden. Regeln werden beispielsweise verwendet, um Formen im Diagramm mit den Modellelementen aktuell zu halten, die sie repräsentieren. Weitere Informationen finden Sie unter Reagieren auf und Verteilen von Änderungen.
Serialisierung: Der Speicher bietet eine Standardmethode für die Serialisierung der darin enthaltenen Objekte in einer Datei. Sie können die Regeln für die Serialisierung und Deserialisierung anpassen. Weitere Informationen finden Sie unter Anpassen von Dateispeicher und XML-Serialisierung.