组件上accessors=true时出现意外行为

11
“我正在尝试在Lucee上使用合成的组件访问器(虽然这个问题在ColdFusion上看起来也是一样的)。
复现代码:”
// Person.cfc
component accessors=true {

    property firstName;
    property lastName;

    function init(firstName, lastName){
        variables.firstName = arguments.firstName;
        variables.lastName = arguments.lastName;
    }

}

而且呼叫代码:

// person.cfm
person = new Person("Abigail", "Bowen");
writeDump(person);

注意我这里没有使用合成的访问器,我纯粹将参数值设置到同名的变量-作用域变量中。
然而当我运行这段代码时,我看到了以下内容:

Dump output showing properties

注意属性已被填充。这没有问题,但我显然不理解accessors标志应该如何工作。我认为它仅仅是为我合成一些访问器方法(它确实做到了),但仅此而已。
另请注意,如果我修改CFC定义,将accessors设置为false,则转储将显示如下内容:

Dump with no properties

因此,没有合成的访问器(如预期的那样),但现在属性甚至没有被显示(无论是使用变量作用域值还是不使用)。

我真的不明白这种“属性”和访问器设置的混淆?访问器设置是否只影响创建那些访问器方法?

如果我只在其中一个平台上看到了这个问题,我可能会归因于writeDump()如何解释属性定义的差异。但是,在ColdFusion 11上的行为是相同的,所以似乎有一些行为上的差异我还没有完全理解。

有人能解释这个吗?有没有任何解释它的文档?如果没有...嗯...为什么没有呢?

我内心的担忧是,属性值没有被“正确地”存储,一旦我实现了更多的代码,就可能会给我带来麻烦。

更新: 至少在ColdFusion上,它似乎只是writeDump()行为的改变,因为如果属性有getter(无论是否设置了accessors标志),则属性值开始出现在dump中。但是,在Lucee上并非如此,因此仍然存在问号。

为了全面公开,这个问题是我在博客上提出的问题的摘要(“CFML: trying to understand accessors”)。这种重复是故意的,因为我的博客受众不同于这个网站。
2个回答

11
没有使用 accessors=trueproperty 声明只是元数据。
使用 accessors=trueproperty 声明会引发 getter / setter 的生成,因此一个 property 同时是一个 variables 作用域项目和一对方法。
在构造函数中,您给 variables 作用域项赋值 -- 这与使用生成的 setter 相同 -- 当 CFML 转储组件时,它会看到 property 元数据和生成的 getters,因此显示这些属性具有的值(因为可以轻松安全地调用生成的 getters)。

1
是的,这确实是它看起来的样子(请参见我的更新)。然而,Lucee的行为与ColdFusion略有不同。我猜Lucee会查找设置和方法;而CF只会查找方法。而且,这种差异完全在转储行为中,与其他任何事情都无关。 - Adam Cameron
这只是CF9构造函数行为的情况吗?在CF9中,init()成为了真正的构造函数,如果你有一个,当你使用new关键字时,它会自动调用,就像Java一样。如果将init()重命名为Person(),应该发生同样的事情。如果你们是正确的,那么当你使用其他函数名称时也会发生这种情况 - 但实际上并没有。只有在CF作为构造函数运行时才会发生这种情况。 - ialexander

2
这个问题出现在ACF9中。在那之前,属性文档中的定义是正确的:cfproperty声明只是元数据(请参见dump(getMetaData()))。
然而,在ACF9中,这种说法不再完全正确,原因有三:
1. 使用accessors=true会为每个属性生成getter和setter,这些访问器会读取和写入变量作用域。Cfproperty不再只是元数据,而是对实例行为产生直接影响。我喜欢把它看作是CF版的真正OO属性(意外引入)。
2. cfdump实现根据属性声明改变其行为。如果定义了“property name;”,并且方法“getName()”存在(已生成或已实现),则将其添加到转储的属性部分中。
3. 属性属性控制ORM。
自从我了解了这些功能,我就会设计所有我的(公共)CFC,以便在转储时看起来正确,例如,我只在想要可见时使用属性声明(+ getters)。此外,您可以实现仅由转储调用且在实例化时不花费任何成本的方法。
struct function getDebug(){
    var x = doSomethingExpensive();
    return { "Foo":f, "Bar":b, "Baz":x };
}

//or for a user iterator
string function getName(){
    return qUsers.name[index];
}

以下是我了解到的一些注意事项:

  • ACF总是从转储中调用getter,而在Railo/Lucee中则显示变量范围中的值。因此,上面的例子(getDebug()getName())在Railo/Lucee上无法工作。
  • 如果getter不是public或者出现错误,则转储会将该属性显示为空字符串(不确定,可能是属性缺失)。
  • 在扩展的CFC中声明的属性会被忽略。这在使用继承的ORM实体中给我带来了一些麻烦,因为你不能声明两次属性。因此,你没有办法显示在基本CFC中定义的属性。
  • Railo/Lucee似乎忽略属性类型。所有访问器只接受和返回字符串(参见getMetaData())。
  • 小问题:在ACF中,当你激活访问器但禁用属性的getter和setter时:property name="user" getter="false" setter="false";它仍然可见于转储 - 应该被隐藏。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接