Verwenden der CSCS-Skriptsprache für die plattformübergreifende Entwicklung
Veröffentlicht: 2022-03-10Unser Ziel ist es nicht, eine Plattform zu bauen; es soll sie alle überfordern.
- Mark Zuckerberg
CSCS (Customized Scripting in C#) ist eine in C# implementierte Open-Source-Skriptsprache. Syntaktisch ist es JavaScript sehr ähnlich, hat aber auch einige Ähnlichkeiten mit Python. Einige dieser Ähnlichkeiten sind die Schlüsselwörter im bekannten if…elif…else
-Konstrukt und haben auch die gleiche Definition des Variablenbereichs wie in Python (z. B. ist eine innerhalb eines if
-Blocks oder innerhalb einer Schleife definierte Variable auch außerhalb sichtbar). .
Im Gegensatz zu JavaScript und Python wird bei Variablen und Funktionen in CSCS die Groß-/Kleinschreibung nicht beachtet. Das primäre Ziel von CSCS ist es, den Entwickler so wenig Code wie möglich schreiben zu lassen . Außerdem wird derselbe Code sowohl für die iOS- als auch für die Android-Entwicklung verwendet. Darüber hinaus kann CSCS für die Windows-, Mac- und Unity-Entwicklung verwendet werden.
Hinweis : Sie können hier mehr darüber lesen, wie Microsoft CSCS in seinem Maquette-Produkt (basierend auf Unity) verwendet.
CSCS kann Ihrem Projekt hinzugefügt werden, indem der C#-Quellcode in ein Visual Studio Xamarin-Projekt eingebettet wird. Im Gegensatz zu den meisten anderen Sprachen besitzen Sie den CSCS-Quellcode vollständig und können seine Funktionalität einfach hinzufügen oder ändern. Ich werde ein Beispiel dafür später in diesem Artikel teilen.
Außerdem werden wir lernen, wie Sie mit CSCS beginnen und einige erweiterte Funktionen verwenden, die in anderen Artikeln behandelt wurden. Unter diesen Features werden wir über Web Requests mit JSON-String-Parsing auf einen Webdienst zugreifen, und wir werden auch SQLite auf iOS und Android verwenden.
Der einfachste Weg, um loszulegen, besteht darin, ein Beispiel eines Projekts mit CSCS herunterzuladen und mit der Datei start.cscs zu spielen. Das werden wir im nächsten Abschnitt tun: Erstellen einer iOS/Android-App mit einfacher GUI und Ereignissen.
"Hallo Welt!" Im CSCS
Beginnen wir mit einem relativ einfachen Beispiel für CSCS-Code, der einen Bildschirm mit einigen Widgets erstellt:
AutoScale(); SetBackgroundColor("light_green"); locLabelText = GetLocation("ROOT", "CENTER", "ROOT", "TOP"); AddLabel(locLabelText, "labelText", "Welcome " + _DEVICE_INFO_ + " " + _VERSION_INFO_ + " User!", 600, 100); locTextEdit = GetLocation("ROOT", "LEFT", labelText, "BOTTOM"); AddTextEdit(locTextEdit, "textEdit", "Your name", 320, 80); locButton = GetLocation(textEdit,"RIGHT",textEdit, "CENTER"); AddButton(locButton, "buttonHi", "Hello", 160, 80); function buttonHi_click(sender, arg) { name = getText(textEdit); msg = name != "" ? "Hello, "+ name + "!" : "Hello, World!"; AlertDialog("My Great App", msg); }
Das folgende Bild zeigt die resultierende Benutzeroberfläche auf einem iPhone sowie einem Android-Gerät, nachdem Sie auf die Schaltfläche „Hallo“ geklickt und nichts in das Feld „Textbearbeitung“ eingegeben haben:
Lassen Sie uns den obigen Code kurz durchgehen. Es beginnt mit dem Funktionsaufruf AutoScale()
, der dem Parser mitteilt, dass die Widget-Größen relativ zur Bildschirmgröße sind, dh sie werden automatisch in der Größe angepasst (das Widget sieht auf größeren Bildschirmen größer und auf kleineren kleiner aus Bildschirme). Diese Einstellung kann auch pro Widget überschrieben werden.
Beachten Sie, dass es nicht erforderlich ist, einen speziellen Handler für einen Klick auf eine Schaltfläche zu erstellen. Wenn Sie eine Funktion mit dem Namen widgetName_click()
definieren, wird sie als Handler verwendet, wenn der Benutzer auf ein Widget namens widgetName
(es muss keine Schaltfläche sein, es kann tatsächlich ein beliebiges Widget sein). Deshalb wird die Funktion buttonHi_click()
ausgelöst, sobald der Benutzer auf die Schaltfläche klickt.
Sie haben vielleicht bemerkt, dass die GUI vollständig aus Code besteht. Dies geschieht, indem beim Hinzufügen ein relativer Widget-Speicherort angegeben wird. Das allgemeine Format eines Ortungsbefehls ist wie folgt:
location = GetLocation(WidgetX, HorizontalPlacement, WidgetY, VerticalPlacement, deltaX=0, deltaY=0, autoResize=true);
So können Sie ein Widget relativ zu anderen Widgets auf dem Bildschirm platzieren. Ein Sonderfall eines Widgets ist ein „ROOT“-Widget, also der Hauptbildschirm.
Nachdem Sie einen Standort erstellt haben, müssen Sie ihn als Argument für eine der folgenden Funktionen bereitstellen:
-
AddLabel
, -
AddButton
, -
AddCombobox
, -
AddStepper
, -
AddListView
, -
AddTextView
, -
AddStepper
, -
AddImageView
, -
AddSlider
, -
AddPickerView
, - und so weiter.
Alle oben genannten haben die gleiche Struktur:
AddButton(location, newWidgetname, initialValue, width, height);
Breite und Höhe des Widgets sind relativ zur Bildschirmgröße, wenn zuvor der CSCS-Befehl AutoScale()
ausgeführt wurde. Außerdem ist der Anfangswert (im Falle einer Schaltfläche) der darauf angezeigte Text. Dies kann jederzeit durch Aufrufen von SetText(widgetName, newText)
.
Verwenden von Visual Studio-Code zum Debuggen von CSCS
Wir können auch Visual Studio Code verwenden, um CSCS-Skripts zu debuggen. Wenn Sie Apps sowohl für Android als auch für iOS entwickeln möchten, müssen Sie einen Mac verwenden. Installieren Sie nach der Installation von Visual Studio Code den CSCS-Debugger und die REPL-Erweiterung.
Um die Erweiterung zu verwenden, fügen Sie diese Codezeile irgendwo in Ihrem CSCS-Skript start.cscs
:
StartDebugger();
Das folgende Bild unten zeigt, wie Sie Visual Studio Code zum Debuggen und Ändern der Funktionalität von „Hello, World!“ verwenden können. App, die wir im vorherigen Abschnitt entwickelt haben. Im nächsten Beispiel fügen wir dem vorhandenen Layout spontan eine Beschriftung und eine Schaltfläche hinzu.
Dazu wählen wir einfach den vom Parser auszuführenden Code aus und drücken Strg + 8 . Als Ergebnis werden eine Beschriftung und eine Schaltfläche in der Mitte des Bildschirms hinzugefügt. Wir fügen auch einen Button-Handler hinzu, der das neue Label mit der aktuellen Zeit bei jedem Klick auf den Button aktualisiert.
Verwenden von SQLite in CSCS
SQLite ist eine relationale Datenbank vom Typ ACID (Atomicity, Consistency, Isolation, Durability) und wurde von Richard Hipp entwickelt (die erste Version wurde im Jahr 2000 veröffentlicht). Im Unterschied zu anderen relationalen Datenbanken, wie Microsoft SQL Server oder Oracle Database, ist sie eingebettet. (Nicht nur in das Gerät, sondern auch in das Endprogramm eingebettet.) Es ist im Programm als sehr kompakte Bibliothek enthalten, die weniger als 500 KB groß ist. Aber zwei Apps (veröffentlicht von demselben Entwickler) können dieselbe SQLite-DB lesen, wenn der DB-Dateipfad beiden Apps bekannt ist.
Der Vorteil von SQLite ist, dass es ohne zusätzliche Installation auf einem iOS- oder einem Android-Gerät verwendet werden kann. Der Nachteil ist, dass er offensichtlich nicht so viele Daten aufnehmen kann wie ein „normaler“ DB und außerdem schwach typisiert ist (dh Sie können anstelle einer Ganzzahl einen String einfügen – er wird dann bei einem Fehler in eine Ganzzahl oder 0 konvertiert). Andererseits kann letzteres auch als Vorteil angesehen werden.
SQLite kann einfach von CSCS ohne zusätzliche Importanweisungen verwendet werden. Hier ist eine Tabelle, die Ihnen hilft, sich einen Überblick über die wichtigsten SQLite-Funktionen zu verschaffen, die in CSCS verwendet werden:
Befehl | Beschreibung |
---|---|
SQLInit(DBName) | Initialisiert eine Datenbank oder legt eine Datenbank fest, die mit nachfolgenden DB-Anweisungen verwendet werden soll. |
SQLDBExists(DBName) | Überprüft, ob der DB initialisiert wurde. Legt auch die Datenbank fest, die mit nachfolgenden DB-Anweisungen verwendet werden soll. |
SQLQuery(query) | Führt eine SQL-Abfrage (eine Select-Anweisung) aus. Gibt eine Tabelle mit Datensätzen zurück. |
SQLNonQuery(nonQuery) | Führt eine SQL-Nichtabfrage aus, z. B. eine Update-, Create- oder Delete-Anweisung. Gibt die Anzahl der betroffenen Datensätze zurück. |
SQLInsert(tableName, columnList, data) | Fügt die übergebene Datentabelle der Datensätze in die angegebene DB-Tabelle ein. Das Argument columnList hat die folgende Struktur: colName1,colName2,…,colNameN |
Tabelle 1: SQLite-Befehle in CSCS
So werden die Funktionen SQLInit()
und SQLDBExists()
typischerweise verwendet:
DBName = "myDB.db1"; if (!SQLDBExists(DBName)) { create = "CREATE TABLE [Data] (Symbol ntext, Low real, High real, Close real, Volume real, Stamp text DEFAULT CURRENT_TIMESTAMP)"; SQLNonQuery(create); } SQLInit(DBName);
Wir werden später weitere Beispiele dafür sehen, wie Sie Daten auswählen und in eine SQLite-Datenbank einfügen können. Ich zeige Ihnen ein Beispiel dafür, wie Sie aus einem Webdienst extrahierte Bestandsdaten in eine lokale SQLite-Datenbank schreiben.
Hinzufügen von benutzerdefinierten Funktionen zu CSCS
In diesem Abschnitt werden wir sehen, wie Sie die CSCS-Funktionalität erweitern können. Als Beispiel sehen wir uns unten die bestehende Implementierung der CSCS-Sleep-Funktion an.
Um benutzerdefinierte Funktionen hinzuzufügen, müssen Sie lediglich eine neue Klasse erstellen, indem Sie von der ParserFunction
-Klasse ableiten, ihre Evaluate()
-Methode überschreiben und diese Klasse beim Parser registrieren. Hier ist eine Kurzversion (ohne Fehlerprüfung):
class SleepFunction : ParserFunction { protected override Variable Evaluate(ParsingScript script) { List args = script.GetFunctionArgs(); int sleepms = Utils.GetSafeInt(args, 0); Thread.Sleep(sleepms); return Variable.EmptyInstance; } }
class SleepFunction : ParserFunction { protected override Variable Evaluate(ParsingScript script) { List args = script.GetFunctionArgs(); int sleepms = Utils.GetSafeInt(args, 0); Thread.Sleep(sleepms); return Variable.EmptyInstance; } }
Die Registrierung einer Klasse beim Parser kann überall in der Initialisierungsphase mit dem folgenden Befehl erfolgen:
ParserFunction.RegisterFunction("Sleep", new SleepFunction());
Das ist es! Nun wird die Evaluate()
Methode der SleepFunction
-Klasse aufgerufen, sobald ein „Sleep“-Token vom Parser extrahiert wird.
Beachten Sie, dass bei CSCS die Groß-/Kleinschreibung nicht beachtet wird (mit Ausnahme der wichtigsten Ablaufsteuerungsanweisungen: if
, elif
, else
, for
, while
, function
, include
, new
, class
, return
, try
, throw
, catch
, break
, continue
). Das bedeutet, dass Sie entweder „sleep(100)“ oder „Sleep(100)“ eingeben können – beide Aufrufe unterbrechen den ausführenden Thread für 100 Millisekunden.
Verarbeitung von JSON in CSCS
JSON (JavaScript Object Notation) ist ein einfaches Datenaustauschformat, das aus Attribut-Wert-Paaren und Array-Typ-Paaren besteht. Es wurde Anfang der 2000er Jahre von Douglas Crockford entwickelt (ungefähr zur gleichen Zeit, als auch SQLite erschien).
In diesem Abschnitt lernen wir, wie man JSON mit CSCS analysiert.
Die CSCS-Funktion zum Analysieren einer JSON-Zeichenfolge ist GetVariableFromJSON(jsonText)
. Diese Funktion gibt eine Hash-Tabelle zurück, in der die Schlüssel die Attribute aus der JSON-Zeichenfolge sind.
Betrachten Sie das folgende Beispiel einer JSON-Zeichenfolge:
jsonString = '{ "eins" : 1, "zwei" : "zweiString", "mehr" : { "uno": "dos" }, "arrayValue" : [ "une", "deux" ] }';
Nach Aufruf:
a = GetVariableFromJSON();
Die Variable a
wird eine Hash-Tabelle mit den folgenden Werten sein:
a["eins"] = 1 a["zwei"] = "zweiString" a["mehr"]["uno"] = "dos" a["arrayValue"][0] = "une" a["arrayValue"][1] = "deux"
Im nächsten Abschnitt sehen wir uns ein weiteres Beispiel für das Parsen einer JSON-Zeichenfolge von einem Webdienst an.
Ein Beispiel für eine App mit SQLite, Webanfragen und JSON
Für eine App, die SQLite, einen Webdienst und JSON-Parsing verwendet, verwenden wir den Alpha Vantage-Webdienst. Sie können einen API-Schlüssel kostenlos erhalten, aber die kostenlose Version erlaubt den Zugriff auf ihren Webdienst nicht mehr als 5 Mal pro Minute.
Mit Alpha Vantage können Sie verschiedene Finanzdatensätze extrahieren – einschließlich Aktienkurse. Dies werden wir in unserer Beispiel-App tun.
Das Bild unten zeigt, wie die Aktien-Apps auf einem iOS- und auf einem Android-Gerät aussehen.
Der CSCS-Code zum Erstellen der GUI lautet wie folgt:
locLabel = GetLocation("ROOT","CENTER", "ROOT","TOP", 0,30); AddLabel(locLabel, "labelRefresh", "", 480, 60); locSFWidget = GetLocation("ROOT","CENTER", labelRefresh,"BOTTOM"); AddSfDataGrid(locSFWidget, "DataGrid", "", graphWidth, graphHeight); listCols = {"Symbol","string", "Low","number", "High", "number", "Close","number", "Volume","number"}; AddWidgetData(DataGrid, listCols, "columns"); colWidth = {17, 19, 19, 19, 26}; AddWidgetData(DataGrid, colWidth, "columnWidth"); locButton = GetLocation("ROOT","CENTER",DataGrid,"BOTTOM"); AddButton(locButton, "buttonRefresh", "Refresh", 160, 80); locLabelError = GetLocation("ROOT","CENTER","ROOT","BOTTOM"); AddLabel(locLabelError, "labelError", "", 600, 160); SetFontColor(labelError, "red"); AlignText(labelError, "center"); getDataFromDB();
Die Methode getDataFromDB()
extrahiert alle Daten aus der SQLite-Datenbank. Es verwendet die wie folgt definierte SQL-Abfrage:
query = "SELECT Symbol, Low, High, Close, Volume, DATETIME(Stamp, 'localtime') as Stamp FROM Data ORDER BY Stamp DESC LIMIT 5;";
Sehen Sie sich den folgenden Code für die Implementierung von getDataFromDB()
an.
function getDataFromDB() { results = SQLQuery(query); for (i = 1; i < results.Size; i++) { vals = results[i]; stock = vals[0]; low = Round(vals[1], 2); high = Round(vals[2], 2); close = Round(vals[3], 2); volume = Round(vals[4], 2); refresh = vals[5]; stockData = {stock, low, high, close, volume}; AddWidgetData(DataGrid, stockData, "item"); } SetText(labelRefresh, "DB Last Refresh: " + refresh); lockGui(false); }
Sehen wir uns nun an, wie wir Daten vom Alpha Vantage Web Service erhalten. Zuerst initialisieren wir die Daten:
baseURL = "https://www.alphavantage.co/query? " + "function=TIME_SERIES_DAILY&symbol="; apikey = "Y12T0TY5EUS6BC5F"; stocks = {"MSFT", "AAPL", "GOOG", "FB", "AMZN"}; totalStocks = stocks.Size;
Als nächstes laden wir Aktien nacheinander, sobald der Benutzer auf die Schaltfläche „Aktualisieren“ klickt:
function buttonRefresh_click(object, arg) { lockGui(); SetText(labelRefresh, "Loading ..."); SetText(labelError, ""); ClearWidget(DataGrid); loadedStocks = 0; getData(stocks[loadedStocks]); } function getData(symbol) { stockUrl = baseURL + symbol + "&apikey=" + apikey; WebRequest("GET", stockUrl, "", symbol, "OnSuccess", "OnFailure"); }
Hier ist die wichtigste CSCS-Funktion, die verwendet werden muss, um Daten von einem Webdienst abzurufen:
WebRequest("GET", stockUrl, "", symbol, "OnSuccess", "OnFailure");
Die letzten beiden Parameter sind Funktionen, die nach Abschluss der Webanforderung aufgerufen werden. Im Fehlerfall wird beispielsweise die folgende CSCS-Funktion aufgerufen:
function OnFailure(object, errorCode, text) { SetText(labelError, text); lockGui(false); }
Als Ergebnis erhält der Benutzer eine Fehlermeldung wie unten gezeigt:
Aber wenn alles gut ist, werden wir den JSON-String parsen und seinen Inhalt in die SQLite-DB einfügen.
function OnSuccess(object, errorCode, text) { jsonFromText = GetVariableFromJSON(text); metaData = jsonFromText[0]; result = jsonFromText[1]; symbol = metaData["2. Symbol"]; lastRefreshed = metaData["3. Last Refreshed"]; allDates = result.keys; dateData = result[allDates[0]]; high = Round(dateData["2. high"], 2); low = Round(dateData["3. low"], 2); close = Round(dateData["4. close"], 2); volume = dateData["5. volume"]; stockData = {symbol, low, high, close, volume}; SQLInsert("Data","Symbol,Low,High,Close,Volume",stockData); if (++loadedStocks >= totalStocks) { getDataFromDB(); } else { getData(stocks[loadedStocks]); } }
Um zu verstehen, wie wir auf verschiedene Felder in der obigen Hash-Tabelle zugreifen, werfen wir einen Blick auf die tatsächliche Zeichenfolge, die wir von der Alpha Vantage-Webanfrage erhalten haben:
{ "Meta Data": { "1. Information": "Daily Prices (open, high, low, close) and Volumes", "2. Symbol": "MSFT", "3. Last Refreshed": "2019-10-02 14:23:20", "4. Output Size": "Compact", "5. Time Zone": "US/Eastern" }, "Time Series (Daily)": { "2019-10-02": { "1. open": "136.3400", "2. high": "136.3700", "3. low": "133.5799", "4. close": "134.4100", "5. volume": "11213086" }, … } }
Wie Sie sehen können, erhalten wir das neueste Datum als erstes Element des allDates
Arrays, das alle extrahierten Daten enthält.
Fazit
Das Hinzufügen von CSCS zu Ihrem Projekt ist einfach. Alles, was Sie tun müssen, ist einfach den Quellcode von CSCS als Modul in Ihr Projekt einzubetten – genau wie in einem Xamarin-Beispielprojekt.
Verwenden und erweitern Sie die CSCS-Skriptsprache in Ihren Projekten? Hinterlasse unten einen Kommentar – ich würde mich freuen, von dir zu hören!
Weiterführende Lektüre
Wenn Sie die CSCS-Sprache ein wenig mehr erkunden möchten, finden Sie hier einige der Artikel, über die ich zu diesem Thema geschrieben habe:
- „Ein Split-and-Merge-Ausdrucksparser in C#“, MSDN Magazine (Okt. 2015)
- „Anpassbare Skripterstellung in C#“, MSDN Magazine (Feb. 2016)
- „Schreiben nativer mobiler Apps mit einer anpassbaren Skriptsprache“, MSDN Magazine (Feb. 2018)
- „CSCS: Angepasstes Scripting in C#“, GitHub
- „Entwicklung plattformübergreifender nativer Apps mit einer funktionalen Skriptsprache“, CODE Magazine
- „Eine benutzerdefinierte Sprache prägnant implementieren“ (eBook)
- „Native Mobile Apps prägnant in einer funktionalen Sprache schreiben“ (eBook)
Als zusätzliche Ressource empfehle ich auch zu lesen, wie Sie die Leistung von CSCS verbessern können, indem Sie seine Funktionen vorkompilieren.