使用 CSCS 脚本语言进行跨平台开发
已发表: 2022-03-10我们的目标不是搭建平台; 这是跨越所有这些。
- 马克·扎克伯格
CSCS (Customized Scripting in C#) 是一种用 C# 实现的开源脚本语言。 在语法上它与 JavaScript 非常相似,但也与 Python 有一些相似之处。 其中一些相似之处是众所周知的if…elif…else
构造中的关键字,并且也具有与 Python 中相同的变量范围定义(例如,在if
块内或循环内定义的变量也将在外部可见) .
与 JavaScript 和 Python 不同,CSCS 中的变量和函数不区分大小写。 CSCS 的主要目标是让开发人员编写尽可能少的代码。 此外,相同的代码用于 iOS 和 Android 开发。 此外,CSCS 可用于 Windows、Mac 和 Unity 开发。
注意:您可以在此处阅读有关 Microsoft 如何在其 Maquette 产品(基于 Unity)中使用 CSCS 的更多信息。
通过将 CSCS 的 C# 源代码嵌入到 Visual Studio Xamarin 项目中,可以将 CSCS 添加到你的项目中。 与大多数其他语言不同,您拥有 CSCS 源代码的完全所有权,并且可以轻松添加或修改其功能。 我将在本文后面分享一个这样的例子。
此外,我们将学习如何开始使用 CSCS并使用其他文章中介绍的一些更高级的功能。 在这些功能中,我们将通过带有 JSON 字符串解析的 Web 请求访问 Web 服务,我们还将在 iOS 和 Android 上使用 SQLite。
最简单的入门方法是下载使用 CSCS 的项目示例并开始使用start.cscs文件。 这就是我们将在下一节中做的事情:创建一个带有基本 GUI 和事件的 iOS/Android 应用程序。
“你好世界!” 在 CSCS
让我们从一个相对简单的 CSCS 代码示例开始,它构建了一个带有一些小部件的屏幕:
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); }
下图显示了在单击“Hello”按钮且未在“文本编辑”字段中输入任何内容后,iPhone 和 Android 设备上生成的用户界面:
让我们简要回顾一下上面的代码。 它从AutoScale()
函数调用开始,它的作用是告诉解析器小部件大小与屏幕大小相关,即它们将自动调整大小(小部件在更大的屏幕上看起来更大,在更小的屏幕上看起来更小屏)。 每个小部件也可以覆盖此设置。
请注意,无需在单击按钮时创建特殊处理程序。 如果你定义了一个名为widgetName_click()
的函数,当用户单击一个名为widgetName
的小部件时,它将用作处理程序(它不必是按钮,它实际上可以是任何小部件)。 这就是为什么一旦用户点击按钮就会触发函数buttonHi_click()
的原因。
您可能已经注意到 GUI 完全是用代码构建的。 这是通过在添加时提供一个相对的小部件位置来完成的。 定位命令的一般格式如下:
location = GetLocation(WidgetX, HorizontalPlacement, WidgetY, VerticalPlacement, deltaX=0, deltaY=0, autoResize=true);
因此,您可以相对于屏幕上的其他小部件放置一个小部件。 小部件的一个特例是“ROOT”小部件,即主屏幕。
创建位置后,您需要将其作为参数提供给以下任何函数:
-
AddLabel
, -
AddButton
, -
AddCombobox
, -
AddStepper
, -
AddListView
, -
AddTextView
, -
AddStepper
, -
AddImageView
, -
AddSlider
, -
AddPickerView
, - 等等。
以上所有具有相同的结构:
AddButton(location, newWidgetname, initialValue, width, height);
如果之前运行了AutoScale()
CSCS 命令,则小部件的宽度和高度将与屏幕大小相关。 此外,初始值(如果是按钮)是其上显示的文本。 这可以通过调用SetText(widgetName, newText)
随时更改。
使用 Visual Studio Code 调试 CSCS
我们还可以使用 Visual Studio Code 来调试 CSCS 脚本。 如果您想开发适用于 Android 和 iOS 的应用程序,则需要使用 Mac。 安装 Visual Studio Code 后,安装 CSCS 调试器和 REPL 扩展。
为了使用扩展,在你的start.cscs
CSCS 脚本的任何地方添加这行代码:
StartDebugger();
下图显示了如何使用 Visual Studio Code 调试和更改“Hello, World!”的功能我们在上一节中开发的应用程序。 在接下来的示例中,我们将动态添加标签和按钮到现有布局。
为此,我们只需选择要由解析器执行的代码,然后按Ctrl + 8 。 结果,将在屏幕中央添加一个标签和一个按钮。 我们还添加了一个按钮处理程序,它将在每次单击按钮时使用当前时间更新新标签。
在 CSCS 中使用 SQLite
SQLite 是一种 ACID(原子性、一致性、隔离性、持久性)类型的关系数据库,由 Richard Hipp 开发(第一个版本于 2000 年发布)。 与其他关系数据库(如 Microsoft SQL Server 或 Oracle 数据库)不同,它是嵌入式的。 (不仅嵌入到设备中,还嵌入到最终程序中。)它作为一个非常紧凑的库包含在程序中,大小不到 500 KB。 但是,如果两个应用程序都知道数据库文件路径,则两个应用程序(由同一开发人员发布)可以读取相同的 SQLite DB。
SQLite 的优点是无需额外安装即可在 iOS 或 Android 设备上使用。 缺点是它显然不能像“普通”数据库那样保存尽可能多的数据,而且它是弱类型的(即你可以插入一个字符串而不是一个整数——然后它将被转换为一个整数或失败时为 0)。 另一方面,后者也可以被视为优势。
无需额外的导入语句即可从 CSCS 轻松使用 SQLite。 下表将帮助您了解 CSCS 中使用的主要 SQLite 函数的概述:
命令 | 描述 |
---|---|
SQLInit(DBName) | 初始化数据库或设置要与后续 DB 语句一起使用的数据库。 |
SQLDBExists(DBName) | 检查 DB 是否已初始化。 还设置要与后续 DB 语句一起使用的数据库。 |
SQLQuery(query) | 执行 SQL 查询(选择语句)。 返回包含记录的表。 |
SQLNonQuery(nonQuery) | 执行 SQL 非查询,例如更新、创建或删除语句。 返回受影响的记录数。 |
SQLInsert(tableName, columnList, data) | 将传递的记录数据表插入到指定的 DB 表中。 columnList 参数具有以下结构: colName1,colName2,…,colNameN |
表 1:CSCS 中的 SQLite 命令
这就是通常使用SQLInit()
和SQLDBExists()
函数的方式:
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);
稍后我们将看到更多关于如何选择数据并将数据插入 SQLite 数据库的示例。 我将向您展示如何将已从 Web 服务提取的股票数据写入本地 SQLite 数据库的示例。
向 CSCS 添加自定义功能
在本节中,我们将了解如何扩展 CSCS 功能。 例如,我们将在下面看到 CSCS 睡眠功能的现有实现。
要添加自定义功能,您需要做的就是通过从ParserFunction
类派生、覆盖其Evaluate()
方法并将该类注册到解析器来创建一个新类。 这是一个简短的版本(没有错误检查):
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; } }
可以在初始化阶段的任何地方通过以下命令向解析器注册类:
ParserFunction.RegisterFunction("Sleep", new SleepFunction());
而已! 现在,一旦解析器提取了“睡眠”标记,就会调用SleepFunction
类的Evaluate()
方法。
请注意,CSCS 不区分大小写(除了核心控制流语句: if
、 elif
、 else
、 for
、 while
、 function
、 include
、 new
、 class
、 return
、 try
、 throw
、 catch
、 break
、 continue
)。 这意味着您可以键入“sleep(100)”或“Sleep(100)”——这两个调用都会将正在执行的线程挂起 100 毫秒。
在 CSCS 中处理 JSON
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,由属性-值对和数组-类型对组成。 它是由 Douglas Crockford 在 2000 年代初开发的(大约在 SQLite 出现的同时)。
在本节中,我们将学习如何使用 CSCS 解析 JSON。
解析 JSON 字符串的 CSCS 函数是GetVariableFromJSON(jsonText)
。 此函数返回一个哈希表,其中键是 JSON 字符串中的属性。
考虑以下 JSON 字符串示例:
jsonString = '{ "eins" : 1, "zwei" : "zweiString", "mehr" : { "uno": "dos" }, "arrayValue" : [ "une", "deux" ] }';
调用后:
a = GetVariableFromJSON();
变量a
将是具有以下值的哈希表:
a["eins"] = 1 a["zwei"] = "zweiString" a["mehr"]["uno"] = "dos" a["arrayValue"][0] = "une" a["arrayValue"][1] = "deux"
在下一节中,我们将看到另一个从 Web 服务解析 JSON 字符串的示例。
带有 SQLite、Web 请求和 JSON 的应用程序示例
对于使用 SQLite、Web 服务和 JSON 解析的应用程序,我们将使用 Alpha Vantage Web 服务。 您可以免费获得 API 密钥,但免费版本允许访问他们的网络服务每分钟不超过 5 次。
使用 Alpha Vantage,您可以提取各种金融数据集——包括股票价格。 这就是我们将在示例应用程序中执行的操作。
下图显示了 Stocks 应用程序在 iOS 和 Android 设备上的外观。
构建 GUI 的 CSCS 代码如下:
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();
getDataFromDB()
方法将从 SQLite 数据库中提取所有数据。 它使用如下定义的 SQL 查询:
query = "SELECT Symbol, Low, High, Close, Volume, DATETIME(Stamp, 'localtime') as Stamp FROM Data ORDER BY Stamp DESC LIMIT 5;";
看看下面的getDataFromDB()
实现代码。
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); }
现在让我们看看我们如何从 Alpha Vantage Web 服务中获取数据。 首先,我们初始化数据:
baseURL = "https://www.alphavantage.co/query? " + "function=TIME_SERIES_DAILY&symbol="; apikey = "Y12T0TY5EUS6BC5F"; stocks = {"MSFT", "AAPL", "GOOG", "FB", "AMZN"}; totalStocks = stocks.Size;
接下来,只要用户点击“刷新”按钮,我们就会一一加载股票:
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"); }
下面是用于从 Web 服务获取数据的主要 CSCS 函数:
WebRequest("GET", stockUrl, "", symbol, "OnSuccess", "OnFailure");
最后两个参数是在完成 Web 请求时调用的函数。 例如,如果发生故障,将调用以下 CSCS 函数:
function OnFailure(object, errorCode, text) { SetText(labelError, text); lockGui(false); }
结果,用户将收到如下所示的错误消息:
但是,如果一切顺利,我们将解析 JSON 字符串并将其内容插入 SQLite DB。
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]); } }
为了了解我们如何访问上面哈希表中的不同字段,让我们看一下从 Alpha Vantage Web 请求中接收到的实际字符串:
{ "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" }, … } }
如您所见,我们将最新日期作为包含所有提取日期的allDates
数组的第一个元素。
结论
将 CSCS 添加到您的项目很容易。 您需要做的只是将 CSCS 的源代码作为模块嵌入到您的项目中——就像在示例 Xamarin 项目中所做的那样。
您是否在项目中使用和扩展 CSCS 脚本语言? 在下面发表评论-我很高兴收到您的来信!
延伸阅读
如果你想更多地探索 CSCS 语言,这里有一些我写过的关于这个主题的文章:
- “C# 中的拆分和合并表达式解析器”,MSDN 杂志(2015 年 10 月)
- “C# 中的可自定义脚本”,MSDN 杂志(2016 年 2 月)
- “使用可自定义的脚本语言编写本机移动应用程序”,MSDN 杂志(2018 年 2 月)
- “CSCS:C# 中的自定义脚本”,GitHub
- “使用函数式脚本语言开发跨平台原生应用程序”,CODE 杂志
- “简洁地实现自定义语言”(电子书)
- “简洁地用函数式语言编写原生移动应用程序”(电子书)
作为附加资源,我还建议您阅读如何通过预编译 CSCS 函数来提高其性能。