aardio教程三) 元表、元方法

来源:https://www.cnblogs.com/kanadeblisst/p/18084708
-Advertisement-
Play Games

前言 還有個迭代器,基礎語法基本已經說完了,後面想到啥再補充,之後的教程會從以下方面來講: 基礎庫的使用,比如string、table等 基礎控制項的使用,比如listview、tab等 aardio和Python交互,比如給Python寫個界面 自帶的範常式序 我寫的一些小程式 當然,我的理解也是很 ...


前言

還有個迭代器,基礎語法基本已經說完了,後面想到啥再補充,之後的教程會從以下方面來講:

  • 基礎庫的使用,比如string、table等
  • 基礎控制項的使用,比如listview、tab等
  • aardio和Python交互,比如給Python寫個界面
  • 自帶的範常式序
  • 我寫的一些小程式

當然,我的理解也是很基礎的,特別是在界面設計上,我都是用的預設控制項的預設設置,不會去自定義控制項內容。要想做出特別炫酷的程式,你還得依賴其他語言和工具的基礎。例如用HTML和CSS來實現界面。

元表、元方法

參考文檔:

  • https://bbs.aardio.com/doc/reference/libraries/kernel/table/meta.html
  • https://bbs.aardio.com/doc/reference/the%20language/operator/overloading.html

主要是用於重載運算符和內置函數的行為。

表可以定義另一個表作為元表,然後在元表裡定義元方法來定義操作符或內置函數操作表的一些行為。這種類似於Python的魔法方法,在Python中使用__eq__定義==的行為,而在aardio中用_eq元方法來定義==的行為。

初級使用例子

舉個例子,python中print會調用對象的__str____repr__來列印一個對象,而aardio也是調用tostring來列印一個對象,但表預設並沒有定義_tostring元方法,導致列印出來的內容是table: 03B2E3A8的格式

我們可以通過給表定義_tostring元方法,來使io.print或者console.log正常顯示表

import console; 
io.open()
var tab = {
	a=1;
	b=2;
	@{
		_tostring = function(...) {
		    // 元方法里不能調用觸發元方法的函數,比如_tostring里不能調用tostring
		    // _get元方法可以通過[[k]]運算符來避開元方法,通過.和[]會觸發_get,而[[]]不會
			return table.tostring(owner);
		}
	}
}
io.print("沒有定義元方法" , {});
io.print("定義了元方法" , tab);
console.pause(true);

輸出如下:

沒有定義元方法  table: 03A8E2F8
定義了元方法    {
a=1;
b=2
}

運算符重載

這個就不細說了,應該很容易理解。

io.open(); tab = { x=10 ; y=20 };
tab2 = { x=12 ; y=22 }
//c = tab + tab2; //這樣肯定會出錯,因為 table預設是不能相加的

//創建一個元素,元表中的__add函數重載加運算符。
tab@ = {
	_add = function(b) { 
		return owner.x + b.x
	};
}

c = tab + tab2; //這時候會調用重載的操作符 tab@._add(tab2)
io.print( c ) //顯示22

入門使用例子

還有一個很常用的元方法是_get_set,是定義訪問對象屬性時觸發的。利用這個可以讓代碼量少很多,看起來邏輯也更清晰。

這裡舉個實際例子,我在封裝sunny的時候,遇到個很累人的事。sunny的dll導出函數,返回值有些是指針,你需要手動給他轉成字元串,而且還需要手動釋放這個指針指向的記憶體,也就是說你調用一次導出函數,就得寫至少三行代碼(調用、轉字元串和釋放)。

那麼,有沒有一種方法,定義完這個導出函數,在使用的時候就調用函數釋放記憶體,並轉成字元串返回,而不用我每次都手動釋放和轉字元串。

先定義request類,現在只需要給它定義一個messageId屬性和_meta元方法:

namespace sunny;

class request{
	ctor(messageId){
		this.messageId = messageId;
	}
	@_meta;
	
}

@後面跟的是元表的名稱,你可以將元表定義在名字空間里,這樣看起來代碼更舒服。下麵在類的名字空間里定義dll方法和元表.

namespace request{
    //釋放指針的函數
	Free = ::SunnyDLL.api("Free","void(pointer p)");
	// 下麵的函數第一個參數都是messageId
    DelRequestHeader = ::SunnyDLL.api("DelRequestHeader","void(int id,str h)");
    GetRequestBodyLen = ::SunnyDLL.api("GetRequestBodyLen","int(int id)");
	GetRequestBody = ::SunnyDLL.api("GetRequestBody","pointer(int id)");
	// 定義一個中間方法
    // name是要調用的導出函數,messageId則是導出函數的第一個參數
	xcall = function(name, messageId, len){
		var func = self[name];
		if(!func) error("不支持的函數!");
		function proxyFunc(...){
			var v = func(messageId, ...);
			var result;
			if(type(v) == type.pointer){
				if(len) result = ..raw.tostring(v,1,len);
				else result = ..raw.tostring(v);
				Free(v);
			}else{
				result = v;
			}
			return result;
		}
		
		return proxyFunc;
	}
	// 定義元表
	_meta = {
		_get = function(k){
			return xcall(k, owner.messageId);
		}
		
	}
}

這個代碼初看可能有點費勁,我們拆解著來看。

首先前面幾行只是定義了四個dll的導出函數,然後下麵定義了_meta這個表。

而_meta里只定義了一個元方法_get,它的作用是當你訪問對象的屬性時會觸發這個方法,然後給你返回值。比如我先實例化一個request對象

r = request(111111);
// 當訪問r.GetRequestBody時,這個對象沒有GetRequestBody屬性,所以會觸發_get元方法
// 得到的返回值就是 返回它的返回值也就是`xcall("GetRequestBody", owner.messageId)`.
console.log(r.GetRequestBody)

這裡的owner就是指r這個對象。然後定義了xcall這個函數,它裡面又定義了一個函數proxyFunc,並將它作為返回值,這種被稱為閉包。先分析下xcall方法

// 這裡的self指的是當前名字空間,也就是request,name則是需要調用的方法名,例如是GetRequestBody
// 這裡func的值就等於GetRequestBody,也就是::SunnyDLL.api("GetRequestBody","pointer(int id)");
var func = self[name];
// 如果func是null的話,說明當前名字空間下沒有這個函數,也就不是我們定義的sunny導出函數
if(!func) error("不支持的函數!");
// 定義了proxyFunc函數,`xcall(k, owner.messageId)`返回的值就是proxyFunc函數,這裡的三個點表示傳入任意個參數,類似於Python中的*args
function proxyFunc(...){
    // 調用GetRequestBody(messageId, ...)
	var v = func(messageId, ...);
	// 定義返回結果
	var result;
	// 如果結果是指針的話
	if(type(v) == type.pointer){
	    // 就把它轉為字元串,二進位數據需要指定長度,不然就是到\0結束
		if(len) result = ..raw.tostring(v,1,len);
		else result = ..raw.tostring(v);
		// 調用導出函數釋放記憶體
		Free(v);
	}else{
	    // 如果是其他類型數據就直接返回,比如數值或null
		result = v;
	}
	return result;
}

這樣一番折騰,起了什麼效果呢,看一下下麵兩段代碼,如果不利用元方法的話,你使用dll導出函數得這麼寫

// 導入request名字空間
improt request;
// 調用名字空間下的函數
var messageId = 111111
var pResult = request.GetRequestBody(messageId);
// 將指針轉為字元串
var result = raw.tostring(pResult,1);
// 釋放記憶體
request.Free(pResult);
// 再使用其他導出函數也需要重覆寫這幾行代碼

看著就幾行代碼,但是你想想調用一個函數都得寫好幾行,如果調用多次呢。而定義了xcall和_meta之後,只需要這樣寫代碼:

improt request;
var messageId = 111111;
var req = request(messageId);
var result = req.GetRequestBody();
// 後面調用都只需要用req.方法名()調用,不需要管raw.tostring和Free了

因為req是可以復用的,所以我調用任何導出函數都只需要寫一行代碼,使用sunny庫的代碼也變得更簡潔易懂了。

官方例子

給表創建一個代理,監控表屬性的訪問和設置:

// 創建一個代理,為另一個table對象創建一個替身以監控對這個對象的訪問
function table.createProxy(tab) {
    var real = tab;//在閉包中保存被代理的數據表tab
    var _meta = {
        _get = function(k){
            io.print(k+"被讀了");
            return real[k];
        };
        _set = function (k,v){
            io.print(k+"被修改值為"+v)
            real[k]=v; //刪除這句代碼就創建了一個只讀表
        }
    }
    var proxy = {@_meta};//創建一個代理表
    
    return proxy; //你要訪問真正的表?先問過我吧,我是他的經紀人!!!
}

//下麵是使用示例

tab = {x=12;y=15};
proxy = table.createProxy(tab);//創建一個代理表,以管理對tab的存取訪問

io.open();
c = proxy.x; //顯示 "x被讀了"
proxy.y = 19; //顯示 "y被修改值為19"
io.print(proxy.y); //顯示 "y被讀了" 然後顯示19

所有的元方法

元方法/屬性 函數定義 Python中的魔法方法 說明
_weak 用不到
_type 屬性 type(obj)函數的行為
_readonly 屬性 等於false,_開頭的成員也不是只讀屬性
_defined 感覺沒啥用
_keys 屬性 可用於table.keys等函數動態獲取對象的鍵名列表(例如動態生成鍵值對的外部JS對象可使用這個元方法返回成員名字列表
_startIndex 屬性 用於table.eachIndex等函數動態指定數組的開始下標。
_get function(k,ownerCall) __getattr____getitem__ 如果讀取表中不存在的鍵會觸發_get元方法並返回值
_set function(k,v) __setattr____setitem__ 當你給表的一個缺少的鍵賦值時會觸發_set元方法
_tostring function(...) __str____repr__ tostring(obj, ...)
_tonumber function() tonumber(obj)
_json function() web.json.stringify(obj),可返回一個可被轉化為json的值。或者返回一個字元串和true
_toComObject 用於自定義一個表對象如何轉換為 COM 對象,可定義為函數,也可以直接定義為對象
_eq function(b) __eq____ne__ ==!=,比較對象時,兩個對象的_eq必須是同一個
_le function(b) __le____ge__ <=>=
_lt function(b) __lt____gt__ <>
_add function(b) __add__ +
_sub function(b) __sub__ -
_mul function(b) __mul__ *
_div function(b) __truediv__ /
_lshift function(b) __lshift__ << 左移
_rshift function(b) __rshift__ >> 右移
_mod function(b) __mod__ % 取模
_pow function(b) __pow__ **冪運算
_unm function() __neg__ - 負號
_len function() __len__ #取長運算符,Python中則為len函數
_concat function(b) ++ 連接運算符
_call function(...) __call__ 對象當函數來調用

屬性元表

不僅可以給對象定義元表,也可以給對象的屬性定義一個元表,有點類似於Python中的property,可以控制屬性修改和獲取的行為。

如果要看例子的話,可以在aardio的目錄全局搜下@_metaProperty

以使用最多的屬性text為例,基本每個控制項都有一個text屬性,你可以很方便的通過.text獲取和修改空間顯示的文字。

其實不用屬性元表也能實現這個效果,代碼如下:

import console; 
class staticText{
	getText = function(){
		..console.log("獲取到界面文本內容")
	};
	setText = function(v){
		..console.log("將文本("+v+")顯示到界面控制項上")
	}
	@_meta;
}

namespace staticText{
    _meta = {
        _get = function(k){
        	if(k == "text"){
        	    return owner.getText();
        	}
        };
        _set = function(k,v){
        	if(k == "text"){
        	    return owner.setText(v);
        	}
        }    
    }
}

s = staticText()
console.log(s.text);
s.text = "修改文本";
console.pause(true);

但是如果屬性多了的話,就需要一堆的if來判斷屬性,所以aardio作者就引入了metaProperty這個功能。這樣寫的代碼看起來更簡潔和清晰,用法如下:

import console; 
import util.metaProperty;

class staticText{
	getText = function(){
		..console.log("獲取到界面文本內容")
	};
	setText = function(v){
		..console.log("將文本("+v+")顯示到界面控制項上")
	}
	@_metaProperty;
}

namespace staticText{
    _metaProperty = ..util.metaProperty(
        text = {
            _get = function(){
            	return owner.getText();
            };
            _set = function(v){
            	return owner.setText(v);
            }
        };
        // 可以寫其他屬性
    );
    // 可以列印下_metaProperty看看
    ..console.dump(_metaProperty);
}

s = staticText()
console.log(s.text);
s.text = "修改文本";
console.pause(true);

本文由博客一文多發平臺 OpenWrite 發佈!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • C++ 簡介 什麼是 C++? C++ 是一種跨平臺的編程語言,可用於創建高性能應用程式。 C++ 是由 Bjarne Stroustrup 開發的,作為 C 語言的擴展。 C++ 為程式員提供了對系統資源和記憶體的高級控制。 該語言在 2011 年、2014 年、2017 年和 2020 年進行了 ...
  • https://leetcode.cn/problems/product-of-array-except-self/description/?envType=study-plan-v2&envId=top-interview-150 問題在於不使用除法並且空間複雜度為O(1),當第一次從頭開始遍歷時 ...
  • 車輛出險報告查詢功能一直以來都是車主們關註的重點,畢竟瞭解一輛車的出險、理賠和事故記錄對於購車和保險的選擇都有著重要影響。而今天,我將向大家介紹一款實用的API介面,通過提供車輛出險報告查詢的功能,幫助車主們快速瞭解車輛的歷史記錄。 這個API介面的使用非常簡單方便,只需要提供車輛的VIN碼和行駛證 ...
  • 你好,這裡是codetrend專欄“SpringCloud2023實戰”。歡迎點擊關註查看往期文章。 註冊中心在前文提到有很多選型,在這裡以Spring Cloud Zookeeper為例說明註冊中心的集成和使用。 選擇Spring Cloud Zookeeper作為註冊中心原因如下: 依賴更少,只 ...
  • 參考指南 fastjson:我一路向北,離開有你的季節 | 素十八 (su18.org) Java 反序列化漏洞始末(3)— fastjson - 淺藍 's blog (b1ue.cn) 梅子酒の筆記本 (meizjm3i.github.io) fastjson基礎 早期版本的 fastjson ...
  • 就在昨晚,Java 22正式發佈!該版本提供了 12 項功能增強,其中包括 7 項預覽功能和 1 項孵化器功能。它們涵蓋了對 Java 語言、API、性能以及 JDK 中包含的工具的改進。 下麵就來一起學習一下該版本都更新了哪些新特性! Unnamed Variables & Patterns - ...
  • 基於Js和Java+MyBatis實現xlsx\xls文檔的導入下載、導出 背景: ​ 實現xlsx\xls文檔的導入、導出 ​ 導入效果: ​ 導出效果: 導出效果圖 1、導入、下載 1.1、前臺 <div style="margin-left: 15px"> <input type="file" ...
  • 本文介紹基於Python中ArcPy模塊,讀取Excel表格數據並生成帶有屬性表的矢量要素圖層,同時配置該圖層的坐標系的方法~ ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...