[英]Could haxe macro be used to detect when object is dirty (any property has been changed)
Let say we have an object:假设我们有一个对象:
@:checkDirty
class Test {
var a:Int;
var b(default, default):String;
var c(get, set):Array<Int>;
public function new() {
...
}
public function get_c() {
...
}
public function set_c(n) {
...
}
}
Could we write a macro checkDirty
so that any change to field/properties would set property dirty
to true
.我们可以写一个宏
checkDirty
以便对字段/属性的任何更改都会将属性dirty
设置为true
。 Macro would generate dirty
field as Bool
and clearDirty
function to set it to false
.宏将生成
dirty
字段作为Bool
和clearDirty
函数以将其设置为false
。
var test = new Test();
trace(test.dirty); // false
test.a = 12;
trace(test.dirty); // true
test.clearDirty();
trace(test.dirty); //false
test.b = "test"
trace(test.dirty); //true
test.clearDirty();
test.c = [1,2,3];
trace(test.dirty); //true
Just to note - whenever you consider proxying access to an object, in my experience, there are always hidden costs / added complexity.请注意 - 每当您考虑代理访问对象时,根据我的经验,总是存在隐藏成本/增加的复杂性。 :)
:)
That said, you have a few approaches:也就是说,您有几种方法:
First, if you want it to be pure Haxe, then either a macro or an abstract can get the job done.首先,如果您希望它是纯 Haxe,那么宏或摘要都可以完成工作。 Either way, you're effectively transforming every property access into a function call that sets the value and also sets
dirty
.无论哪种方式,您都有效地将每个属性访问转换为一个函数调用,该函数调用设置值并设置
dirty
。
For example, an abstract using the @:resolve
getter and setter can be found in the NME source code , replicated here for convenience:例如,可以在NME 源代码中找到使用
@:resolve
getter 和 setter 的摘要,为方便起见,复制到此处:
@:forward(decode,toString)
abstract URLVariables(URLVariablesBase)
{
public function new(?inEncoded:String)
{
this = new URLVariablesBase(inEncoded);
}
@:resolve
public function set(name:String, value:String) : String
{
return this.set(name,value);
}
@:resolve
public function get(name:String):String
{
return this.get(name);
}
}
This may be an older syntax, I'm not sure... also look at the operator overloading examples on the Haxe manual:这可能是一个较旧的语法,我不确定......还可以查看 Haxe 手册上的运算符重载示例:
@:op(a.b) public function fieldRead(name:String)
return this.indexOf(name);
@:op(a.b) public function fieldWrite(name:String, value:String)
return this.split(name).join(value);
Second, I'd just point out that if the underlying language / runtime supports some kind of Proxy object (eg JavaScript Proxy ), and macro / abstract isn't working as expected, then you could build your functionality on top of that.其次,我只想指出,如果底层语言/运行时支持某种 Proxy 对象(例如JavaScript Proxy ),并且宏/抽象没有按预期工作,那么您可以在此基础上构建您的功能。
I wrote a post ( archive ) about doing this kind of thing (except for emitting events) before - you can use a @:build
macro to modify class members, be it appending an extra assignment into setter or replacing the field with a property.我之前写了一篇关于做这种事情(除了发出事件)的帖子( 存档) - 您可以使用
@:build
宏来修改类成员,无论是将额外的赋值附加到 setter 还是用属性替换字段。
So a modified version might look like so:因此,修改后的版本可能如下所示:
class Macro {
public static macro function build():Array<Field> {
var fields = Context.getBuildFields();
for (field in fields.copy()) { // (copy fields so that we don't go over freshly added ones)
switch (field.kind) {
case FVar(fieldType, fieldExpr), FProp("default", "default", fieldType, fieldExpr):
var fieldName = field.name;
if (fieldName == "dirty") continue;
var setterName = "set_" + fieldName;
var tmp_class = macro class {
public var $fieldName(default, set):$fieldType = $fieldExpr;
public function $setterName(v:$fieldType):$fieldType {
$i{fieldName} = v;
this.dirty = true;
return v;
}
};
for (mcf in tmp_class.fields) fields.push(mcf);
fields.remove(field);
case FProp(_, "set", t, e):
var setter = Lambda.find(fields, (f) -> f.name == "set_" + field.name);
if (setter == null) continue;
switch (setter.kind) {
case FFun(f):
f.expr = macro { dirty = true; ${f.expr}; };
default:
}
default:
}
}
if (Lambda.find(fields, (f) -> f.name == "dirty") == null) fields.push((macro class {
public var dirty:Bool = false;
}).fields[0]);
return fields;
}
}
which, if used as其中,如果用作
@:build(Macro.build())
@:keep class Some {
public function new() {}
public var one:Int;
public var two(default, set):String;
function set_two(v:String):String {
two = v;
return v;
}
}
Would emit the following JS:将发出以下 JS:
var Some = function() {
this.dirty = false;
};
Some.prototype = {
set_two: function(v) {
this.dirty = true;
this.two = v;
return v;
}
,set_one: function(v) {
this.one = v;
this.dirty = true;
return v;
}
};
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.